Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dd9bc0449 | ||
|
|
ced04a3060 | ||
|
|
605f87b078 | ||
|
|
2e083b59a6 | ||
|
|
00f70e7c5e |
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -1,12 +1,9 @@
|
||||
[submodule "Source/Horizon-OC-Monitor/lib/Atmosphere-libs"]
|
||||
path = Source/Horizon-OC-Monitor/lib/Atmosphere-libs
|
||||
url = https://github.com/Atmosphere-NX/Atmosphere-libs
|
||||
branch = master
|
||||
[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
|
||||
branch = main
|
||||
[submodule "Source/sys-clk/overlay/lib/libultrahand"]
|
||||
path = Source/sys-clk/overlay/lib/libultrahand
|
||||
url = https://github.com/ppkantorski/libultrahand
|
||||
52
README.md
52
README.md
@@ -1,7 +1,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
<img src="assets/logo.png" alt="logo" width="768"/>
|
||||
<img src="assets/logo.png" alt="logo" width="350"/>
|
||||
|
||||
---
|
||||
|
||||
@@ -32,16 +32,16 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
|
||||
|
||||
---
|
||||
|
||||
## Default clocks
|
||||
## Features
|
||||
|
||||
* **CPU:** Up to 1963MHz (Mariko) / 1785MHz (Erista)
|
||||
* **GPU:** Up to 1075MHz (Mariko) / 921MHz (Erista)
|
||||
* **RAM:** Up to 1866/2133MHz (Mariko) / 1600MHz (Erista)
|
||||
* **RAM:** Up to 1866MHz (Mariko) / 1600MHz (Erista)
|
||||
* Over/undervolting support
|
||||
* Built-in configurator
|
||||
* Compatible with most homebrew
|
||||
|
||||
> It is recommended to read the [guide](https://rentry.co/howtoget60fps) before proceeding, as this can help you get a *significant* performance boost over the default settings, often times with less power draw and heat output
|
||||
> It is reccomended to read the [guide](https://rentry.co/howtoget60fps) before proceeding, as this can help you get a *significant* performance boost over the default settings, often times with less power draw and heat output
|
||||
|
||||
---
|
||||
|
||||
@@ -93,8 +93,7 @@ Refer to COMPILATION.md
|
||||
* 665
|
||||
|
||||
### CPU clocks
|
||||
* 2703 → mariko absolute max, dangerous
|
||||
* 2601 → unsafe
|
||||
* 2601 → mariko absolute max, very dangerous
|
||||
* 2499
|
||||
* 2397 → mariko safe max with UV (low speedo)
|
||||
* 2295
|
||||
@@ -114,6 +113,9 @@ Refer to COMPILATION.md
|
||||
* 816
|
||||
* 714
|
||||
* 612 → sleep mode
|
||||
|
||||
**Notes:**
|
||||
1. On Erista, CPU in handheld is capped to 1581MHz
|
||||
|
||||
### GPU clocks
|
||||
* 1536 → absolute max clock on mariko. very dangerous
|
||||
@@ -140,33 +142,27 @@ Refer to COMPILATION.md
|
||||
* 76 → boost mode
|
||||
|
||||
**Notes:**
|
||||
1. On Erista, CPU in handheld is capped to 1581MHz
|
||||
2. GPU overclock is capped at 460MHz on erista in handheld
|
||||
3. On Mariko, cap with No uv is 614MHz, with SLT it is 691MHz and with HiOPT it's 768MHz
|
||||
4. Clocks higher than 768MHz on erista need the official charger is plugged in.
|
||||
5. On Mariko, cap with No uv is 844MHz, with SLT it is 921MHz and with HiOPT it's 998MHz
|
||||
|
||||
1. GPU overclock is capped at 460MHz on erista in handheld
|
||||
2. On Mariko, cap with No uv is 614MHz, with SLT it is 691MHz and with HiOPT it's 768MHz
|
||||
3. Clocks higher than 768MHz on erista need the official charger is plugged in.
|
||||
4. On Mariko, cap with No uv is 844MHz, with SLT it is 921MHz and with HiOPT it's 998MHz
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
* **Lightos's Cat** - Cat
|
||||
|
||||
* **Souldbminer** - hoc-clk and loader development
|
||||
* **Lightos** - Loader patches development, hoc-clk development, guides
|
||||
* **Souldbminer** – hoc-clk and loader development
|
||||
* **Lightos** – loader patches development
|
||||
* **SciresM** - Atmosphere CFW
|
||||
* **CTCaer** - L4T, Hekate, proper RAM timings
|
||||
* **KazushiMe** - Switch OC Suite
|
||||
* **Hanai3bi (Meha)** - Switch OC Suite, EOS, sys-clk-eos
|
||||
* **NaGaa95** - L4T-OC kernel, Status Monitor fork
|
||||
* **B3711 (halop)** - EOS
|
||||
* **sys-clk team (m4xw, p-sam, natinusala)** - sys-clk
|
||||
* **Dominatorul** - Soctherm driver, guides, general help
|
||||
* **b0rd2death** - Ultrahand sys-clk & Status Monitor fork
|
||||
* **CTCaer** - L4T, Hekate, perfect ram timings
|
||||
* **KazushiMe** – Switch OC Suite
|
||||
* **hanai3bi (meha)** – Switch OC Suite, EOS, sys-clk-eos
|
||||
* **NaGaa95** – L4T-OC-kernel
|
||||
* **B3711 (halop)** – EOS
|
||||
* **sys-clk team (m4xw, p-sam, natinusala)** – sys-clk
|
||||
* **b0rd2death** – Ultrahand sys-clk & Status Monitor fork
|
||||
* **MasaGratoR and ZachyCatGames** - General help
|
||||
* **MasaGratoR** - Status Monitor & Display Refresh Rate driver
|
||||
* **Dominatorul, Samybigio, Arcdelta, Miki, Happy, Flopsider, Winnerboi77, Blaise, Alvise, TDRR, agjeococh, frost, letum00, and Xenshen** - Testing
|
||||
* **Samybigio2011, Miki** - Italian translations
|
||||
* **angelblaster** - Korean translations
|
||||
* **q1332348216-glitch** - Chinese translations
|
||||
* **Nvidia** - [Tegra X1 Technical Reference Manual](https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual), soctherm driver, L4T
|
||||
* **MasaGratoR** - Status Monitor & Display Refresh Rate Driver
|
||||
* **Dom, Samybigio, Arcdelta, Miki, Happy, Flopsider, Winnerboi77, Blaise, Alvise, TDRR, agjeococh, frost, letum00 and Xenshen** - Testing
|
||||
* **Samybigio2011** - Italian translations
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.x | :white_check_mark: |
|
||||
| 0.x | Not supported |
|
||||
| 0.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) ppkantorski (bord2death)
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@@ -12,14 +12,14 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#define __ACCESS_TABLE_NAME__ SocthermAccessTable
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceSoctherm.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_soctherm_access_table_data.inc"
|
||||
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include <cstdio>
|
||||
namespace notification {
|
||||
void writeNotification(const std::string& message);
|
||||
}
|
||||
#include "secmon_define_access_table.inc"
|
||||
|
||||
#undef __ACCESS_TABLE_INC__
|
||||
#undef __ACCESS_TABLE_ADDRESS__
|
||||
#undef __ACCESS_TABLE_NAME__
|
||||
@@ -123,7 +123,7 @@ namespace ams::secmon {
|
||||
constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCode = MemoryRegion(UINT64_C(0x40020000), 0x20000);
|
||||
static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramBootCode));
|
||||
|
||||
constexpr inline const MemoryRegion MemoryRegionVirtualDevice = MemoryRegion(UINT64_C(0x1F0040000), UINT64_C(0x40000));
|
||||
constexpr inline const MemoryRegion MemoryRegionVirtualDevice = MemoryRegion(UINT64_C(0x1F0040000), UINT64_C(0x40000 + 0x2000));
|
||||
static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDevice));
|
||||
|
||||
constexpr inline const MemoryRegion MemoryRegionVirtualDeviceEmpty = MemoryRegion(MemoryRegionVirtualDevice.GetStartAddress(), 0);
|
||||
@@ -155,6 +155,9 @@ namespace ams::secmon {
|
||||
HANDLER(Disp1, Sdmmc, UINT64_C(0x54200000), UINT64_C(0x3000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Dsi, Disp1, UINT64_C(0x54300000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(MipiCal, Dsi, UINT64_C(0x700E3000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Soctherm, MipiCal, UINT64_C(0x700E2000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(ExternalMemoryController1, Soctherm, UINT64_C(0x7001e000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(ExternalMemoryController2, ExternalMemoryController1, UINT64_C(0x7001f000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
|
||||
#define DEFINE_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \
|
||||
constexpr inline const MemoryRegion MemoryRegionVirtualDevice##_NAME_ = MemoryRegion(MemoryRegionVirtualDevice##_PREV_.GetEndAddress() + 0x1000, _SIZE_); \
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_misc.hpp"
|
||||
#include "secmon_smc_common.hpp"
|
||||
#include "secmon_smc_handler.hpp"
|
||||
#include "secmon_smc_aes.hpp"
|
||||
#include "secmon_smc_carveout.hpp"
|
||||
#include "secmon_smc_device_unique_data.hpp"
|
||||
#include "secmon_smc_error.hpp"
|
||||
#include "secmon_smc_info.hpp"
|
||||
#include "secmon_smc_memory_access.hpp"
|
||||
#include "secmon_smc_power_management.hpp"
|
||||
#include "secmon_smc_random.hpp"
|
||||
#include "secmon_smc_register_access.hpp"
|
||||
#include "secmon_smc_result.hpp"
|
||||
#include "secmon_smc_rsa.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct HandlerInfo {
|
||||
u32 function_id;
|
||||
u32 restriction_mask;
|
||||
SmcHandler handler;
|
||||
};
|
||||
|
||||
struct HandlerTable {
|
||||
const HandlerInfo *entries;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
enum HandlerType : int {
|
||||
HandlerType_User = 0,
|
||||
HandlerType_Kern = 1,
|
||||
HandlerType_Count = 2,
|
||||
};
|
||||
|
||||
enum Restriction {
|
||||
Restriction_None = (0 << 0),
|
||||
Restriction_Normal = (1 << 0),
|
||||
Restriction_DeviceUniqueDataNotAllowed = (1 << 1),
|
||||
Restriction_SafeModeNotAllowed = (1 << 2),
|
||||
};
|
||||
|
||||
enum SmcCallRange {
|
||||
SmcCallRange_ArmArch = 0,
|
||||
SmcCallRange_Cpu = 1,
|
||||
SmcCallRange_Sip = 2,
|
||||
SmcCallRange_Oem = 3,
|
||||
SmcCallRange_Standard = 4,
|
||||
|
||||
SmcCallRange_TrustedApp = 0x30,
|
||||
};
|
||||
|
||||
enum SmcArgumentType {
|
||||
ArgumentType_Integer = 0,
|
||||
ArgumentType_Pointer = 1,
|
||||
};
|
||||
|
||||
enum SmcConvention {
|
||||
Convention_Smc32 = 0,
|
||||
Convention_Smc64 = 1,
|
||||
};
|
||||
|
||||
enum SmcCallType {
|
||||
SmcCallType_YieldingCall = 0,
|
||||
SmcCallType_FastCall = 1,
|
||||
};
|
||||
|
||||
struct SmcFunctionId {
|
||||
using FunctionId = util::BitPack64::Field< 0, 8, u32>;
|
||||
using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>;
|
||||
using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>;
|
||||
using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>;
|
||||
using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>;
|
||||
using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>;
|
||||
using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>;
|
||||
using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>;
|
||||
using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>;
|
||||
using Reserved = util::BitPack64::Field<16, 8, u32>;
|
||||
using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>;
|
||||
using Convention = util::BitPack64::Field<30, 1, SmcConvention>;
|
||||
using CallType = util::BitPack64::Field<31, 1, SmcCallType>;
|
||||
using Reserved2 = util::BitPack64::Field<32, 32, u32>;
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_user_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig },
|
||||
{ 0xC3000002, Restriction_Normal, SmcGetConfigUser },
|
||||
{ 0xC3000003, Restriction_Normal, SmcGetResult },
|
||||
{ 0xC3000404, Restriction_Normal, SmcGetResultData },
|
||||
{ 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate },
|
||||
{ 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes },
|
||||
{ 0xC3000007, Restriction_Normal, SmcGenerateAesKek },
|
||||
{ 0xC3000008, Restriction_Normal, SmcLoadAesKey },
|
||||
{ 0xC3000009, Restriction_Normal, SmcComputeAes },
|
||||
{ 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey },
|
||||
{ 0xC300040B, Restriction_Normal, SmcComputeCmac },
|
||||
{ 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData },
|
||||
{ 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData },
|
||||
{ 0xC300000E, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
|
||||
{ 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
|
||||
{ 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
|
||||
{ 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey }
|
||||
};
|
||||
|
||||
/* Deprecated handlerss. */
|
||||
constexpr inline const HandlerInfo DecryptAndImportEsDeviceKeyHandlerInfo = {
|
||||
0xC300100C, Restriction_Normal, SmcDecryptAndImportEsDeviceKey
|
||||
};
|
||||
|
||||
constexpr inline const HandlerInfo DecryptAndImportLotusKeyHandlerInfo = {
|
||||
0xC300100E, Restriction_SafeModeNotAllowed, SmcDecryptAndImportLotusKey
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_kern_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu },
|
||||
{ 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu },
|
||||
{ 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu },
|
||||
{ 0xC3000004, Restriction_Normal, SmcGetConfigKern },
|
||||
{ 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking },
|
||||
{ 0xC3000006, Restriction_Normal, SmcShowError },
|
||||
{ 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion },
|
||||
{ 0xC3000008, Restriction_Normal, SmcReadWriteRegister },
|
||||
|
||||
/* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */
|
||||
{ 0xC3000409, Restriction_Normal, SmcSetConfig },
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_ams_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xF0000201, Restriction_None, SmcIramCopy },
|
||||
{ 0xF0000002, Restriction_None, SmcReadWriteRegister },
|
||||
{ 0xF0000003, Restriction_None, SmcWriteAddress },
|
||||
{ 0xF0000404, Restriction_None, SmcGetEmummcConfig },
|
||||
{ 0xF0000005, Restriction_None, SmcShowError },
|
||||
};
|
||||
|
||||
constexpr const HandlerInfo GetSecureDataHandlerInfo = {
|
||||
0x67891234, Restriction_None, SmcGetSecureData
|
||||
};
|
||||
|
||||
constinit HandlerTable g_handler_tables[] = {
|
||||
{ g_user_handlers, util::size(g_user_handlers) },
|
||||
{ g_kern_handlers, util::size(g_kern_handlers) },
|
||||
};
|
||||
|
||||
constinit HandlerTable g_ams_handler_table = {
|
||||
g_ams_handlers, util::size(g_ams_handlers)
|
||||
};
|
||||
|
||||
NORETURN void InvalidSmcError(u64 id) {
|
||||
SetError(pkg1::ErrorInfo_UnknownSmc);
|
||||
AMS_ABORT("Invalid SMC: %lx", id);
|
||||
}
|
||||
|
||||
const HandlerTable &GetHandlerTable(HandlerType type, u64 id) {
|
||||
/* Ensure we have a valid handler type. */
|
||||
if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Provide support for legacy SmcGetSecureData. */
|
||||
if (id == GetSecureDataHandlerInfo.function_id) {
|
||||
return g_handler_tables[HandlerType_User];
|
||||
}
|
||||
|
||||
/* Check if we're a user SMC. */
|
||||
if (type == HandlerType_User) {
|
||||
/* Nintendo uses OEM SMCs. */
|
||||
/* We will assign Atmosphere extension SMCs the TrustedApplication range. */
|
||||
if (util::BitPack64{id}.Get<SmcFunctionId::CallRange>() == SmcCallRange_TrustedApp) {
|
||||
return g_ams_handler_table;
|
||||
}
|
||||
|
||||
/* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */
|
||||
if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
}
|
||||
|
||||
return g_handler_tables[type];
|
||||
}
|
||||
|
||||
const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) {
|
||||
/* Provide support for legacy SmcGetSecureData. */
|
||||
if (id == GetSecureDataHandlerInfo.function_id) {
|
||||
return GetSecureDataHandlerInfo;
|
||||
}
|
||||
|
||||
/* Get and check the index. */
|
||||
const auto index = util::BitPack64{id}.Get<SmcFunctionId::FunctionId>();
|
||||
if (AMS_UNLIKELY(index >= table.count)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Get and check the handler info. */
|
||||
const auto &handler_info = table.entries[index];
|
||||
|
||||
/* Check that the handler isn't null. */
|
||||
if (AMS_UNLIKELY(handler_info.handler == nullptr)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Check that the handler's id matches. */
|
||||
if (AMS_UNLIKELY(handler_info.function_id != id)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
return handler_info;
|
||||
}
|
||||
|
||||
bool IsHandlerRestricted(const HandlerInfo &info) {
|
||||
return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0;
|
||||
}
|
||||
|
||||
SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) {
|
||||
/* Check if the smc is restricted. */
|
||||
if (GetTargetFirmware() >= TargetFirmware_8_0_0 && AMS_UNLIKELY(IsHandlerRestricted(info))) {
|
||||
return SmcResult::NotPermitted;
|
||||
}
|
||||
|
||||
/* Invoke the smc. */
|
||||
return info.handler(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ConfigureSmcHandlersForTargetFirmware() {
|
||||
const auto target_fw = GetTargetFirmware();
|
||||
|
||||
if (target_fw < TargetFirmware_5_0_0) {
|
||||
g_user_handlers[DecryptAndImportEsDeviceKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportEsDeviceKeyHandlerInfo;
|
||||
g_user_handlers[DecryptAndImportLotusKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportLotusKeyHandlerInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleSmc(int type, SmcArguments &args) {
|
||||
/* Get the table. */
|
||||
const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]);
|
||||
|
||||
/* Get the handler info. */
|
||||
const auto &info = GetHandlerInfo(table, args.r[0]);
|
||||
|
||||
/* Set the invocation result. */
|
||||
args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -101,6 +101,7 @@ namespace ams::secmon::smc {
|
||||
#include "secmon_define_emc_access_table.inc"
|
||||
#include "secmon_define_emc1_access_table.inc"
|
||||
#include "secmon_define_emc2_access_table.inc"
|
||||
#include "secmon_define_soctherm_access_table.inc"
|
||||
#include "secmon_define_mc01_access_table.inc"
|
||||
|
||||
constexpr const AccessTableEntry AccessTables[] = {
|
||||
@@ -109,6 +110,7 @@ namespace ams::secmon::smc {
|
||||
{ EmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController.GetAddress(), EmcAccessTable::Address, EmcAccessTable::Size, },
|
||||
{ EmcAccessTable1::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController1.GetAddress(), EmcAccessTable1::Address, EmcAccessTable1::Size, },
|
||||
{ EmcAccessTable2::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController2.GetAddress(), EmcAccessTable2::Address, EmcAccessTable2::Size, },
|
||||
{ SocthermAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceSoctherm.GetAddress(), SocthermAccessTable::Address, SocthermAccessTable::Size, },
|
||||
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, },
|
||||
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, },
|
||||
};
|
||||
|
||||
1040
Source/Atmosphere-Patches/secmon_soctherm_access_table_data.inc
Normal file
1040
Source/Atmosphere-Patches/secmon_soctherm_access_table_data.inc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* 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.
|
||||
@@ -93,8 +95,6 @@ namespace ams::ldr {
|
||||
|
||||
struct ProcessInfo {
|
||||
os::NativeHandle process_handle;
|
||||
uintptr_t code_address;
|
||||
size_t total_size;
|
||||
uintptr_t args_address;
|
||||
size_t args_size;
|
||||
uintptr_t nso_address[Nso_Count];
|
||||
@@ -106,16 +106,7 @@ namespace ams::ldr {
|
||||
bool has_main;
|
||||
bool has_sdk;
|
||||
bool has_subsdk;
|
||||
s8 nso_indices[Nso_Count];
|
||||
};
|
||||
|
||||
struct AutoLoadModuleContext {
|
||||
NsoHeader *headers;
|
||||
int nso_count;
|
||||
int rtld_idx;
|
||||
int main_nso_idx;
|
||||
int sdk_nso_idx;
|
||||
AutoLoadModuleInfo ali;
|
||||
bool has_nso[Nso_Count];
|
||||
};
|
||||
|
||||
/* Global NSO header cache. */
|
||||
@@ -125,10 +116,6 @@ namespace ams::ldr {
|
||||
bool g_is_pcv;
|
||||
bool g_is_ptm;
|
||||
|
||||
/* Global Zstd decompression context. */
|
||||
constexpr size_t ZstdDctxWorkspaceSize = 0x176E8;
|
||||
alignas(8) u8 g_zstd_dctx_workspace[ZstdDctxWorkspaceSize];
|
||||
|
||||
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
|
||||
/* No version verification is done before 8.1.0. */
|
||||
R_SUCCEED_IF(hos::GetVersion() < hos::Version_8_1_0);
|
||||
@@ -189,15 +176,10 @@ namespace ams::ldr {
|
||||
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
|
||||
}
|
||||
|
||||
Result LoadAutoLoadHeaders(AutoLoadModuleContext &ctx, u32 acid_flags) {
|
||||
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, AutoLoadModuleInfo *ali, u32 acid_flags) {
|
||||
/* Clear NSOs. */
|
||||
std::memset(g_nso_headers, 0, sizeof(g_nso_headers));
|
||||
ctx.headers = g_nso_headers;
|
||||
ctx.nso_count = 0;
|
||||
ctx.rtld_idx = -1;
|
||||
ctx.main_nso_idx = -1;
|
||||
ctx.sdk_nso_idx = -1;
|
||||
ctx.ali = {};
|
||||
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
|
||||
*ali = {};
|
||||
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
/* Only load browser DLLs if acid flags say to do so. */
|
||||
@@ -224,18 +206,16 @@ namespace ams::ldr {
|
||||
|
||||
/* Read NSO header. */
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, g_nso_headers + ctx.nso_count, sizeof(NsoHeader)));
|
||||
R_UNLESS(read_size == sizeof(NsoHeader), ldr::ResultInvalidNso());
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, nso_headers + i, sizeof(*nso_headers)));
|
||||
R_UNLESS(read_size == sizeof(*nso_headers), ldr::ResultInvalidNso());
|
||||
|
||||
/* Note nso is present. */
|
||||
switch (i) {
|
||||
case Nso_Rtld:
|
||||
ctx.rtld_idx = ctx.nso_count;
|
||||
ctx.ali.has_rtld = true;
|
||||
ali->has_rtld = true;
|
||||
break;
|
||||
case Nso_Main:
|
||||
ctx.main_nso_idx = ctx.nso_count;
|
||||
ctx.ali.has_main = true;
|
||||
ali->has_main = true;
|
||||
break;
|
||||
case Nso_SubSdk0:
|
||||
case Nso_SubSdk1:
|
||||
@@ -247,65 +227,64 @@ namespace ams::ldr {
|
||||
case Nso_SubSdk7:
|
||||
case Nso_SubSdk8:
|
||||
case Nso_SubSdk9:
|
||||
ctx.ali.has_subsdk = true;
|
||||
ali->has_subsdk = true;
|
||||
break;
|
||||
case Nso_Sdk:
|
||||
ctx.sdk_nso_idx = ctx.nso_count;
|
||||
ctx.ali.has_sdk = true;
|
||||
ali->has_sdk = true;
|
||||
break;
|
||||
}
|
||||
ctx.ali.nso_indices[ctx.nso_count] = static_cast<s8>(i);
|
||||
ctx.nso_count++;
|
||||
ali->has_nso[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckAutoLoad(const AutoLoadModuleContext &ctx, u32 acid_flags) {
|
||||
Result CheckAutoLoad(const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, u32 acid_flags) {
|
||||
/* We must always have a main. */
|
||||
R_UNLESS(ctx.ali.has_main, ldr::ResultInvalidNso());
|
||||
R_UNLESS(ali->has_main, ldr::ResultInvalidNso());
|
||||
|
||||
/* Validate flags and extents for all present NSOs. */
|
||||
for (int i = 0; i < ctx.nso_count; ++i) {
|
||||
const auto &hdr = ctx.headers[i];
|
||||
/* All NSOs must not be --X. */
|
||||
/* This is "probably" not checked on Ounce? */
|
||||
for (size_t i = 0; i < Nso_Count; ++i) {
|
||||
R_UNLESS((nso_headers[i].flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* All NSOs must not be --X. */
|
||||
/* This is "probably" not checked on Ounce? */
|
||||
R_UNLESS((hdr.flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
|
||||
/* If we don't have an RTLD, we must only have a main. */
|
||||
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
|
||||
if (!ali->has_rtld) {
|
||||
/* If don't have rtld, we must also not have sdk. */
|
||||
R_UNLESS(!ali->has_sdk, ldr::ResultInvalidNso());
|
||||
|
||||
/* Zstd compression only allowed on main, and only when both rtld+sdk are present. */
|
||||
if (i != ctx.main_nso_idx || ctx.rtld_idx < 0 || ctx.sdk_nso_idx < 0) {
|
||||
R_UNLESS((hdr.flags & NsoHeader::Flag_UseZbicCompression) == 0, ldr::ResultInvalidNso());
|
||||
/* We must also not have both subsdk and browser dll. */
|
||||
R_UNLESS(!(ali->has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
|
||||
} else {
|
||||
/* If we have rtld, we must not have browser core dll. */
|
||||
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* Check NSO extents. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
/* Only validate the nsos we have. */
|
||||
if (!ali->has_nso[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* NSOs must have page-aligned segments. */
|
||||
R_UNLESS(util::IsAligned(hdr.text_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(hdr.ro_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(hdr.rw_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].text_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].ro_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].rw_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
|
||||
/* NSOs must have zero text offset. */
|
||||
R_UNLESS(hdr.text_dst_offset == 0, ldr::ResultInvalidNso());
|
||||
R_UNLESS(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso());
|
||||
|
||||
/* NSO .text must precede .rodata. */
|
||||
const size_t text_end = static_cast<size_t>(hdr.text_dst_offset) + static_cast<size_t>(hdr.text_size);
|
||||
R_UNLESS(text_end <= static_cast<size_t>(hdr.ro_dst_offset), ldr::ResultInvalidNso());
|
||||
const size_t text_end = static_cast<size_t>(nso_headers[i].text_dst_offset) + static_cast<size_t>(nso_headers[i].text_size);
|
||||
R_UNLESS(text_end <= static_cast<size_t>(nso_headers[i].ro_dst_offset), ldr::ResultInvalidNso());
|
||||
|
||||
/* NSO .rodata must precede .rwdata. */
|
||||
const size_t ro_end = static_cast<size_t>(hdr.ro_dst_offset) + static_cast<size_t>(hdr.ro_size);
|
||||
R_UNLESS(ro_end <= static_cast<size_t>(hdr.rw_dst_offset), ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
|
||||
if (ctx.ali.has_rtld || ctx.ali.has_sdk) {
|
||||
/* If we have sdk we must have rtld. */
|
||||
R_UNLESS(ctx.ali.has_rtld, ldr::ResultInvalidNso());
|
||||
|
||||
/* If we have rtld, we must not have browser core dll. */
|
||||
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
|
||||
} else {
|
||||
/* We must not have both subsdk and browser dll. */
|
||||
R_UNLESS(!(ctx.ali.has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
|
||||
const size_t ro_end = static_cast<size_t>(nso_headers[i].ro_dst_offset) + static_cast<size_t>(nso_headers[i].ro_size);
|
||||
R_UNLESS(ro_end <= static_cast<size_t>(nso_headers[i].rw_dst_offset), ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -321,8 +300,8 @@ namespace ams::ldr {
|
||||
{ 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */
|
||||
{ 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */
|
||||
{ 0x01006F8002326000 }, /* Animal Crossing: New Horizons */
|
||||
{ 0x01006FB00F50E000 }, /* 宝可梦 走吧!伊布 [Pokemon: Let's Go, Eevee! for China] */
|
||||
{ 0x010070300F50C000 }, /* 宝可梦 走吧!皮卡丘 [Pokemon: Let's Go, Pikachu! for China] */
|
||||
{ 0x01006FB00F50E000 }, /* [???] */
|
||||
{ 0x010070300F50C000 }, /* [???] */
|
||||
{ 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */
|
||||
{ 0x01008DB008C2C000 }, /* Pokemon Shield */
|
||||
{ 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */
|
||||
@@ -546,7 +525,7 @@ namespace ams::ldr {
|
||||
return rand % (max + 1);
|
||||
}
|
||||
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument) {
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
|
||||
/* Clear output. */
|
||||
out->args_address = 0;
|
||||
out->args_size = 0;
|
||||
@@ -557,33 +536,35 @@ namespace ams::ldr {
|
||||
bool argument_allocated = false;
|
||||
|
||||
/* Calculate base offsets. */
|
||||
for (int i = 0; i < ctx.nso_count; i++) {
|
||||
out->nso_address[i] = total_size;
|
||||
const size_t text_end = static_cast<size_t>(ctx.headers[i].text_dst_offset) + static_cast<size_t>(ctx.headers[i].text_size);
|
||||
const size_t ro_end = static_cast<size_t>(ctx.headers[i].ro_dst_offset) + static_cast<size_t>(ctx.headers[i].ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(ctx.headers[i].rw_dst_offset) + static_cast<size_t>(ctx.headers[i].rw_size);
|
||||
out->nso_size[i] = text_end;
|
||||
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
|
||||
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
|
||||
out->nso_size[i] += static_cast<size_t>(ctx.headers[i].bss_size);
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (ali->has_nso[i]) {
|
||||
out->nso_address[i] = total_size;
|
||||
const size_t text_end = static_cast<size_t>(nso_headers[i].text_dst_offset) + static_cast<size_t>(nso_headers[i].text_size);
|
||||
const size_t ro_end = static_cast<size_t>(nso_headers[i].ro_dst_offset) + static_cast<size_t>(nso_headers[i].ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(nso_headers[i].rw_dst_offset) + static_cast<size_t>(nso_headers[i].rw_size);
|
||||
out->nso_size[i] = text_end;
|
||||
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
|
||||
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
|
||||
out->nso_size[i] += static_cast<size_t>(nso_headers[i].bss_size);
|
||||
|
||||
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());
|
||||
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());
|
||||
|
||||
out->nso_size[i] = aligned_up_size;
|
||||
out->nso_size[i] = aligned_up_size;
|
||||
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->nso_size[i]), ldr::ResultInvalidNso());
|
||||
total_size += out->nso_size[i];
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->nso_size[i]), ldr::ResultInvalidNso());
|
||||
total_size += out->nso_size[i];
|
||||
|
||||
if (!argument_allocated && argument != nullptr) {
|
||||
out->args_address = total_size;
|
||||
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
|
||||
if (!argument_allocated && argument != nullptr) {
|
||||
out->args_address = total_size;
|
||||
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
|
||||
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->args_size), ldr::ResultInvalidNso());
|
||||
total_size += out->args_size;
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->args_size), ldr::ResultInvalidNso());
|
||||
total_size += out->args_size;
|
||||
|
||||
argument_allocated = true;
|
||||
argument_allocated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,9 +609,11 @@ namespace ams::ldr {
|
||||
|
||||
/* Set out. */
|
||||
aslr_start += aslr_slide;
|
||||
for (int i = 0; i < ctx.nso_count; i++) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->nso_address[i], aslr_start), ldr::ResultInvalidNso());
|
||||
out->nso_address[i] += aslr_start;
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (ali->has_nso[i]) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->nso_address[i], aslr_start), ldr::ResultInvalidNso());
|
||||
out->nso_address[i] += aslr_start;
|
||||
}
|
||||
}
|
||||
if (out->args_address) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->args_address, aslr_start), ldr::ResultInvalidNso());
|
||||
@@ -639,88 +622,69 @@ namespace ams::ldr {
|
||||
|
||||
out_param->code_address = aslr_start;
|
||||
out_param->code_num_pages = total_size >> 12;
|
||||
out->total_size = total_size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModuleSegment(fs::FileHandle file, size_t file_offset, size_t compressed_size, size_t segment_size, bool is_compressed, bool is_zstd, uintptr_t map_base, uintptr_t map_end) {
|
||||
Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) {
|
||||
/* Select read size based on compression. */
|
||||
size_t file_size = is_compressed ? compressed_size : segment_size;
|
||||
if (!is_compressed) {
|
||||
file_size = segment->size;
|
||||
}
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(file_size <= segment_size, ldr::ResultInvalidNso());
|
||||
R_UNLESS(file_size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
R_UNLESS(segment_size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
R_UNLESS(file_size <= segment->size, ldr::ResultInvalidNso());
|
||||
R_UNLESS(segment->size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
|
||||
/* Load data from file. */
|
||||
uintptr_t load_address = is_compressed ? map_end - compressed_size : map_base;
|
||||
uintptr_t load_address = is_compressed ? map_end - file_size : map_base;
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, file_offset, reinterpret_cast<void *>(load_address), file_size));
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, segment->file_offset, reinterpret_cast<void *>(load_address), file_size));
|
||||
R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
|
||||
|
||||
/* Uncompress if necessary. */
|
||||
R_SUCCEED_IF(!is_compressed);
|
||||
|
||||
auto compressed_data_buf = reinterpret_cast<const void *>(load_address);
|
||||
|
||||
if (is_zstd) {
|
||||
bool decompressed = util::DecompressZstdForLoader(reinterpret_cast<void *>(g_zstd_dctx_workspace), ZstdDctxWorkspaceSize, reinterpret_cast<void *>(map_base), static_cast<size_t>(map_end - map_base), segment_size, compressed_data_buf, file_size);
|
||||
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||
} else {
|
||||
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment_size, compressed_data_buf, file_size) == static_cast<int>(segment_size));
|
||||
if (is_compressed) {
|
||||
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment->size, reinterpret_cast<const void *>(load_address), file_size) == static_cast<int>(segment->size));
|
||||
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* Check hash if necessary. */
|
||||
if (check_hash) {
|
||||
u8 hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::GenerateSha256(hash, sizeof(hash), reinterpret_cast<void *>(map_base), segment->size);
|
||||
|
||||
R_UNLESS(std::memcmp(hash, file_hash, sizeof(hash)) == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckSegmentHash(const NsoHeader *nso_header, uintptr_t map_address, NsoHeader::Segment segment) {
|
||||
if ((nso_header->flags & (NsoHeader::Flag_CheckHashText << segment)) == 0) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u8 hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::GenerateSha256(hash, sizeof(hash),
|
||||
reinterpret_cast<void *>(map_address + nso_header->segments[segment].dst_offset),
|
||||
nso_header->segments[segment].size);
|
||||
R_UNLESS(std::memcmp(hash, nso_header->segment_hashes[segment], sizeof(hash)) == 0, ldr::ResultInvalidNso());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) {
|
||||
/* Map and read data from file. */
|
||||
{
|
||||
/* Map the process memory. */
|
||||
void *mapped_memory = nullptr;
|
||||
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, map_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, map_size); };
|
||||
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); };
|
||||
|
||||
const uintptr_t map_address = reinterpret_cast<uintptr_t>(mapped_memory);
|
||||
const uintptr_t map_end = map_address + map_size;
|
||||
|
||||
/* Load NSO segments. */
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Text].file_offset, nso_header->text_compressed_size, nso_header->text_size,
|
||||
(nso_header->flags & NsoHeader::Flag_CompressedText) != 0, is_zstd, map_address + nso_header->text_dst_offset, map_end));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Ro].file_offset, nso_header->ro_compressed_size, nso_header->ro_size,
|
||||
(nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, is_zstd, map_address + nso_header->ro_dst_offset, map_end));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Rw].file_offset, nso_header->rw_compressed_size, nso_header->rw_size,
|
||||
(nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, is_zstd, map_address + nso_header->rw_dst_offset, map_end));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
|
||||
|
||||
/* Clear unused space to zero. */
|
||||
const size_t text_end = static_cast<size_t>(nso_header->text_dst_offset) + static_cast<size_t>(nso_header->text_size);
|
||||
const size_t ro_end = static_cast<size_t>(nso_header->ro_dst_offset) + static_cast<size_t>(nso_header->ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(nso_header->rw_dst_offset) + static_cast<size_t>(nso_header->rw_size);
|
||||
std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
|
||||
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_size - rw_end);
|
||||
|
||||
/* Check segment hashes. */
|
||||
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Text));
|
||||
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Ro));
|
||||
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Rw));
|
||||
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size);
|
||||
|
||||
/* Apply embedded patches. */
|
||||
ApplyEmbeddedPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
@@ -747,31 +711,25 @@ namespace ams::ldr {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute));
|
||||
}
|
||||
if (ro_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||
}
|
||||
if (rw_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument) {
|
||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
|
||||
/* Load each NSO. */
|
||||
const uintptr_t total_end = process_info->code_address + process_info->total_size;
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (ali->has_nso[i]) {
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
for (int i = 0; i < ctx.nso_count; i++) {
|
||||
const NsoIndex nso_idx = static_cast<NsoIndex>(ctx.ali.nso_indices[i]);
|
||||
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(nso_idx), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
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, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Load arguments, if present. */
|
||||
@@ -797,13 +755,13 @@ namespace ams::ldr {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Get CreateProcessParameter. */
|
||||
svc::CreateProcessParameter param;
|
||||
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
||||
|
||||
/* Decide on an NSO layout. */
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), ctx, argument));
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, ali, argument));
|
||||
|
||||
/* Actually create process. */
|
||||
svc::Handle process_handle;
|
||||
@@ -811,11 +769,10 @@ namespace ams::ldr {
|
||||
|
||||
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
|
||||
out->process_handle = process_handle;
|
||||
out->code_address = param.code_address;
|
||||
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
||||
|
||||
/* Load all auto load modules. */
|
||||
R_RETURN(LoadAutoLoadModules(out, ctx, argument));
|
||||
R_RETURN(LoadAutoLoadModules(out, nso_headers, ali, argument));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -856,13 +813,13 @@ namespace ams::ldr {
|
||||
}
|
||||
|
||||
/* Load, validate NSO headers. */
|
||||
AutoLoadModuleContext ctx;
|
||||
R_TRY(LoadAutoLoadHeaders(ctx, meta.acid->flags));
|
||||
R_TRY(CheckAutoLoad(ctx, meta.acid->flags));
|
||||
AutoLoadModuleInfo auto_load_info = {};
|
||||
R_TRY(LoadAutoLoadHeaders(g_nso_headers, std::addressof(auto_load_info), meta.acid->flags));
|
||||
R_TRY(CheckAutoLoad(g_nso_headers, std::addressof(auto_load_info), meta.acid->flags));
|
||||
|
||||
/* Actually create the process and load NSOs into process memory. */
|
||||
ProcessInfo info;
|
||||
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), ctx, argument, flags, resource_limit));
|
||||
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, std::addressof(auto_load_info), argument, flags, resource_limit));
|
||||
|
||||
/* Register NSOs with the RoManager. */
|
||||
{
|
||||
@@ -874,8 +831,10 @@ namespace ams::ldr {
|
||||
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::AddressSpaceType_64Bit || as_type == Npdm::AddressSpaceType_64BitDeprecated);
|
||||
|
||||
/* Register all NSOs. */
|
||||
for (int i = 0; i < ctx.nso_count; i++) {
|
||||
RoManager::GetInstance().AddNso(pin_id, ctx.headers[i].module_id, info.nso_address[i], info.nso_size[i]);
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (auto_load_info.has_nso[i]) {
|
||||
RoManager::GetInstance().AddNso(pin_id, g_nso_headers[i].module_id, info.nso_address[i], info.nso_size[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#define DISABLED 0
|
||||
#define DEACTIVATED_GPU_FREQ 2000
|
||||
#define GPU_MIN_MIN_VOLT 480000
|
||||
#define CPU_MAX_MAX_VOLT 1200000
|
||||
#define CPU_MAX_MAX_VOLT 1395000
|
||||
|
||||
namespace ams::ldr::hoc {
|
||||
|
||||
@@ -34,7 +34,7 @@ volatile CustomizeTable C = {
|
||||
/* Disables RAM powerdown */
|
||||
.hpMode = DISABLED,
|
||||
|
||||
.commonEmcMemVolt = 1175000, /* LPDDR4(X) JEDEC Specification */
|
||||
.commonEmcMemVolt = 1175000, /* LPDDR4X JEDEC Specification */
|
||||
.eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */
|
||||
.eristaEmcMaxClock1 = 1600000,
|
||||
.eristaEmcMaxClock2 = 1600000,
|
||||
@@ -80,31 +80,33 @@ volatile CustomizeTable C = {
|
||||
.marikoCpuUVLow = 0, // No undervolt
|
||||
.marikoCpuUVHigh = 0, // No undervolt
|
||||
|
||||
.tableConf = TBREAK_1683,
|
||||
.tableConf = DEFAULT_TABLE,
|
||||
.marikoCpuLowVmin = 620,
|
||||
.marikoCpuHighVmin = 750,
|
||||
/* 1120mV is NVIDIA rating */
|
||||
.marikoCpuMaxVolt = 1120,
|
||||
|
||||
/* Supported values: 1963500, 2091000, 2193000, 2295000, 2397000, 2499000, 2601000, 2703000. */
|
||||
/* 1963500 is official rating of T214/Mariko, fully safe. */
|
||||
/* Supported values: 1963000, 2091000, 2193000, 2295000, 2397000, 2499000, 2601000, 2703000. */
|
||||
/* 1963000 is official rating of T214/Mariko, fully safe. */
|
||||
/* 2091000-2295000 is a slight OC which should work on all units, but no guarantees. */
|
||||
/* 2397000 is the max safe OC for most average units with tuned undervolt. */
|
||||
/* 2499000 should be used with caution. */
|
||||
/* 2601000 exceeds pmic limit on most consoles. */
|
||||
/* 2703000 is potentially dangerous and not advised. */
|
||||
.marikoCpuMaxClock = 1963500,
|
||||
.marikoCpuMaxClock = 1963000,
|
||||
|
||||
.eristaCpuBoostClock = 1785000, // Default boost clock
|
||||
.marikoCpuBoostClock = 1963500, // Default boost clock
|
||||
.marikoCpuBoostClock = 1963000, // Default boost clock
|
||||
|
||||
.eristaGpuUV = 0,
|
||||
.eristaGpuVmin = 810,
|
||||
|
||||
.marikoGpuUV = 0,
|
||||
|
||||
/* Vmin past 795mV won't work due boot voltage being 800mV. */
|
||||
.marikoGpuVmin = 610,
|
||||
/* For automatic vmin detection, set this to AUTO. */
|
||||
/* vmin past 795mV won't work due to HOS limitation */
|
||||
/* Vmin is automatically set to 800mV when SoC temperature is below 20C */
|
||||
.marikoGpuVmin = AUTO,
|
||||
|
||||
.marikoGpuVmax = 800,
|
||||
|
||||
@@ -120,32 +122,28 @@ volatile CustomizeTable C = {
|
||||
|
||||
.eristaGpuVoltArray = {
|
||||
AUTO /* 76 */,
|
||||
AUTO /* 115 */,
|
||||
AUTO /* 153 */,
|
||||
AUTO /* 192 */,
|
||||
AUTO /* 230 */,
|
||||
AUTO /* 269 */,
|
||||
AUTO /* 307 */,
|
||||
AUTO /* 346 */,
|
||||
AUTO /* 384 */,
|
||||
AUTO /* 422 */,
|
||||
AUTO /* 460 */,
|
||||
AUTO /* 499 */,
|
||||
AUTO /* 537 */,
|
||||
AUTO /* 576 */,
|
||||
AUTO /* 614 */,
|
||||
AUTO /* 652 */,
|
||||
AUTO /* 691 */,
|
||||
AUTO /* 729 */,
|
||||
AUTO /* 768 */,
|
||||
AUTO /* 806 */,
|
||||
AUTO /* 844 */,
|
||||
AUTO /* 883 */,
|
||||
AUTO /* 921 */,
|
||||
DEACTIVATED_GPU_FREQ /* 960 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 998 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1036 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1075 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1152 (SLT / HiOPT Only!) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1228 (HiOPT Only!) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1305 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1344 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1382 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1420 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1459 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1497 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1536 (Disabled by default) */,
|
||||
},
|
||||
|
||||
.marikoGpuVoltArray = {
|
||||
@@ -165,7 +163,6 @@ volatile CustomizeTable C = {
|
||||
AUTO /* 1075 */,
|
||||
AUTO /* 1152 (SLT / HiOPT Only!) */,
|
||||
AUTO /* 1228 (HiOPT Only!) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1267 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1305 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1344 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1382 (Disabled by default) */,
|
||||
@@ -173,6 +170,14 @@ volatile CustomizeTable C = {
|
||||
DEACTIVATED_GPU_FREQ /* 1459 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1497 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1536 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1574 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1612 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1651 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1689 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1728 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1766 (Disabled by default) */,
|
||||
DEACTIVATED_GPU_FREQ /* 1804 (Disabled by default) */,
|
||||
|
||||
},
|
||||
|
||||
/* Advanced. */
|
||||
@@ -197,9 +202,9 @@ volatile CustomizeTable C = {
|
||||
{ 1581000, { 1130000, }, { 2889664, -122173, 1834, } },
|
||||
{ 1683000, { 1168000, }, { 5100873, -279186, 4747, } },
|
||||
{ 1785000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||
{ 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||
{ 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
{ 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
|
||||
{ 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||
{ 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
{ 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
|
||||
},
|
||||
|
||||
.eristaCpuDvfsTableSLT = {
|
||||
@@ -223,8 +228,11 @@ volatile CustomizeTable C = {
|
||||
{ 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
{ 2091000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
{ 2193000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
{ 2295000, { 1256250, }, { 5100873, -279186, 4747, } },
|
||||
{ 2397000, { 1256250, }, { 5100873, -279186, 4747, } }, // Only for god speedo!
|
||||
{ 2295000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
{ 2397000, { 1395000, }, { 5100873, -279186, 4747, } },
|
||||
{ 2499000, { 1395000, }, { 6000000, -179186, 4747, } },
|
||||
{ 2601000, { 1395000, }, { 6000000, -179186, 4747, } },
|
||||
{ 2703000, { 1395000, }, { 6000000, -179186, 4747, } },
|
||||
},
|
||||
|
||||
.marikoCpuDvfsTable = {
|
||||
@@ -246,17 +254,13 @@ volatile CustomizeTable C = {
|
||||
{ 1785000, { 1527196, -36015, 27, }, { 1120000, } },
|
||||
{ 1887000, { 1609246, -37515, 27, }, { 1120000, } },
|
||||
{ 1963500, { 1675751, -38635, 27, }, { 1120000, } },
|
||||
// { 2091000, { 1716501, -39395, 27, }, { CPU_MAX_MAX_VOLT, } },
|
||||
// { 2193000, { 1775132, -40505, 27, }, { CPU_MAX_MAX_VOLT, } },
|
||||
// { 2295000, { 1866287, -42005, 27, }, { CPU_MAX_MAX_VOLT, } },
|
||||
// { 2397000, { 1961107, -43506, 27, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2091000, { 1716501, -39395, 27, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2193000, { 1775132, -40505, 27, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2295000, { 1866287, -42005, 27, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2397000, { 1961107, -43506, 27, }, { CPU_MAX_MAX_VOLT, } },
|
||||
},
|
||||
|
||||
.marikoCpuDvfsTableSLT = {
|
||||
{ 204000, { 732856, -17335, 113, }, { } },
|
||||
{ 306000, { 760024, -18195, 113, }, { } },
|
||||
{ 408000, { 789258, -19055, 113, }, { } },
|
||||
{ 510000, { 789258, -19055, 113, }, { } },
|
||||
{ 612000, { 789258, -19055, 113, }, { } },
|
||||
{ 714000, { 789258, -19055, 113, }, { } },
|
||||
{ 816000, { 789258, -19055, 113, }, { } },
|
||||
@@ -278,13 +282,12 @@ volatile CustomizeTable C = {
|
||||
{ 2499000, { 1580725, -35815, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2601000, { 1702903, -36675, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2703000, { 1770375, -37515, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2805000, { 1908891, -37707, 113 }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2907000, { 1960388, -38395, 113 }, { CPU_MAX_MAX_VOLT ,} },
|
||||
{ 3009000, { 2011885, -39083, 113 }, { CPU_MAX_MAX_VOLT ,} },
|
||||
},
|
||||
|
||||
.marikoCpuDvfsTable1581Tbreak {
|
||||
{ 204000, { 732856, -17335, 113, }, { } },
|
||||
{ 306000, { 760024, -18195, 113, }, { } },
|
||||
{ 408000, { 789258, -19055, 113, }, { } },
|
||||
{ 510000, { 789258, -19055, 113, }, { } },
|
||||
{ 612000, { 853926, -20775, 113, }, { } },
|
||||
{ 714000, { 889361, -21625, 113, }, { } },
|
||||
{ 816000, { 926862, -22485, 113, }, { } },
|
||||
@@ -306,13 +309,12 @@ volatile CustomizeTable C = {
|
||||
{ 2499000, { 1736856, -35286, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2601000, { 1787838, -35967, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2805000, { 1908891, -37707, 113 }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2907000, { 1960388, -38395, 113 }, { CPU_MAX_MAX_VOLT ,} },
|
||||
{ 3009000, { 2011885, -39083, 113 }, { CPU_MAX_MAX_VOLT ,} },
|
||||
},
|
||||
|
||||
.marikoCpuDvfsTable1683Tbreak {
|
||||
{ 204000, { 732856, -17335, 113, }, { } },
|
||||
{ 306000, { 760024, -18195, 113, }, { } },
|
||||
{ 408000, { 789258, -19055, 113, }, { } },
|
||||
{ 510000, { 789258, -19055, 113, }, { } },
|
||||
{ 612000, { 853926, -20775, 113, }, { } },
|
||||
{ 714000, { 889361, -21625, 113, }, { } },
|
||||
{ 816000, { 926862, -22485, 113, }, { } },
|
||||
@@ -334,13 +336,12 @@ volatile CustomizeTable C = {
|
||||
{ 2499000, { 1736856, -35286, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2601000, { 1787838, -35967, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2805000, { 1908891, -37707, 113 }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2907000, { 1960388, -38395, 113 }, { CPU_MAX_MAX_VOLT ,} },
|
||||
{ 3009000, { 2011885, -39083, 113 }, { CPU_MAX_MAX_VOLT ,} },
|
||||
},
|
||||
|
||||
.marikoCpuDvfsTableExtreme {
|
||||
{ 204000, { 732856, -17335, 113, }, { } },
|
||||
{ 306000, { 760024, -18195, 113, }, { } },
|
||||
{ 408000, { 789258, -19055, 113, }, { } },
|
||||
{ 510000, { 789258, -19915, 113, }, { } },
|
||||
{ 612000, { 789258, -19055, 113, }, { } },
|
||||
{ 714000, { 820558, -19915, 113, }, { } },
|
||||
{ 816000, { 853926, -20775, 113, }, { } },
|
||||
@@ -362,91 +363,66 @@ volatile CustomizeTable C = {
|
||||
{ 2499000, { 1580725, -35815, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2601000, { 1702903, -36675, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2703000, { 1775375, -37515, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2805000, { 1908891, -37707, 113 }, { CPU_MAX_MAX_VOLT, } },
|
||||
{ 2907000, { 1960388, -38395, 113 }, { CPU_MAX_MAX_VOLT ,} },
|
||||
{ 3009000, { 2011885, -39083, 113 }, { CPU_MAX_MAX_VOLT ,} },
|
||||
},
|
||||
|
||||
.eristaGpuDvfsTable = {
|
||||
{ 76800, { }, { 814294, 8144, -940, 808, -21583, 226, } },
|
||||
{ 115200, { }, { 856185, 8144, -940, 808, -21583, 226, } },
|
||||
{ 153600, { }, { 856185, 8144, -940, 808, -21583, 226, } },
|
||||
{ 192000, { }, { 898077, 8144, -940, 808, -21583, 226, } },
|
||||
{ 230400, { }, { 898077, 8144, -940, 808, -21583, 226, } },
|
||||
{ 268800, { }, { 939968, 8144, -940, 808, -21583, 226, } },
|
||||
{ 307200, { }, { 939968, 8144, -940, 808, -21583, 226, } },
|
||||
{ 345600, { }, { 981860, 8144, -940, 808, -21583, 226, } },
|
||||
{ 384000, { }, { 981860, 8144, -940, 808, -21583, 226, } },
|
||||
{ 422400, { }, { 1023751, 8144, -940, 808, -21583, 226, } },
|
||||
{ 460800, { }, { 1023751, 8144, -940, 808, -21583, 226, } },
|
||||
{ 499200, { }, { 1065642, 8144, -940, 808, -21583, 226, } },
|
||||
{ 537600, { }, { 1065642, 8144, -940, 808, -21583, 226, } },
|
||||
{ 576000, { }, { 1107534, 8144, -940, 808, -21583, 226, } },
|
||||
{ 614400, { }, { 1107534, 8144, -940, 808, -21583, 226, } },
|
||||
{ 652800, { }, { 1149425, 8144, -940, 808, -21583, 226, } },
|
||||
{ 691200, { }, { 1149425, 8144, -940, 808, -21583, 226, } },
|
||||
{ 729600, { }, { 1191317, 8144, -940, 808, -21583, 226, } },
|
||||
{ 768000, { }, { 1191317, 8144, -940, 808, -21583, 226, } },
|
||||
{ 806400, { }, { 1233208, 8144, -940, 808, -21583, 226, } },
|
||||
{ 844800, { }, { 1233208, 8144, -940, 808, -21583, 226, } },
|
||||
{ 883200, { }, { 1275100, 8144, -940, 808, -21583, 226, } },
|
||||
{ 921600, { }, { 1275100, 8144, -940, 808, -21583, 226, } },
|
||||
// { 998400, { }, { 1316991, 8144, -940, 808, -21583, 226, } },
|
||||
},
|
||||
|
||||
.eristaGpuDvfsTableSLT = {
|
||||
{ 76800, { }, { 814294, 8144, -940, 0, 0, 226, } },
|
||||
{ 115200, { }, { 856185, 8144, -940, 0, 0, 226, } },
|
||||
{ 153600, { }, { 856185, 8144, -940, 0, 0, 226, } },
|
||||
{ 192000, { }, { 908077, 8144, -940, 0, 0, 226, } },
|
||||
{ 230400, { }, { 908077, 8144, -940, 0, 0, 226, } },
|
||||
{ 268800, { }, { 934968, 8144, -940, 0, 0, 226, } },
|
||||
{ 307200, { }, { 934968, 8144, -940, 0, 0, 226, } },
|
||||
{ 345600, { }, { 952860, 8144, -940, 0, 0, 226, } },
|
||||
{ 384000, { }, { 952860, 8144, -940, 0, 0, 226, } },
|
||||
{ 422400, { }, { 978751, 8144, -940, 0, 0, 226, } },
|
||||
{ 460800, { }, { 978751, 8144, -940, 0, 0, 226, } },
|
||||
{ 499200, { }, { 990642, 8144, -940, 0, 0, 226, } },
|
||||
{ 537600, { }, { 990642, 8144, -940, 0, 0, 226, } },
|
||||
{ 576000, { }, { 1017534, 8144, -940, 0, 0, 226, } },
|
||||
{ 614400, { }, { 1017534, 8144, -940, 0, 0, 226, } },
|
||||
{ 652800, { }, { 1042425, 8144, -940, 0, 0, 226, } },
|
||||
{ 691200, { }, { 1042425, 8144, -940, 0, 0, 226, } },
|
||||
{ 729600, { }, { 1066317, 8144, -940, 0, 0, 226, } },
|
||||
{ 768000, { }, { 1066317, 8144, -940, 0, 0, 226, } },
|
||||
{ 806400, { }, { 1093208, 8144, -940, 0, 0, 226, } },
|
||||
{ 844800, { }, { 1093208, 8144, -940, 0, 0, 226, } },
|
||||
{ 883200, { }, { 1118100, 8144, -940, 0, 0, 226, } },
|
||||
{ 921600, { }, { 1118100, 8144, -940, 0, 0, 226, } },
|
||||
{ 960000, { }, { 1156991, 8144, -940, 0, 0, 226, } },
|
||||
},
|
||||
|
||||
.eristaGpuDvfsTableHiOPT = {
|
||||
{ 76800, { }, { 814294, 8144, -940, 0, 0, 226, } },
|
||||
{ 115200, { }, { 856185, 8144, -940, 0, 0, 226, } },
|
||||
{ 153600, { }, { 856185, 8144, -940, 0, 0, 226, } },
|
||||
{ 192000, { }, { 908077, 8144, -940, 0, 0, 226, } },
|
||||
{ 230400, { }, { 908077, 8144, -940, 0, 0, 226, } },
|
||||
{ 268800, { }, { 934968, 8144, -940, 0, 0, 226, } },
|
||||
{ 307200, { }, { 934968, 8144, -940, 0, 0, 226, } },
|
||||
{ 345600, { }, { 952860, 8144, -940, 0, 0, 226, } },
|
||||
{ 384000, { }, { 952860, 8144, -940, 0, 0, 226, } },
|
||||
{ 422400, { }, { 978751, 8144, -940, 0, 0, 226, } },
|
||||
{ 460800, { }, { 978751, 8144, -940, 0, 0, 226, } },
|
||||
{ 499200, { }, { 990642, 8144, -940, 0, 0, 226, } },
|
||||
{ 537600, { }, { 990642, 8144, -940, 0, 0, 226, } },
|
||||
{ 576000, { }, { 1017534, 8144, -940, 0, 0, 226, } },
|
||||
{ 614400, { }, { 1017534, 8144, -940, 0, 0, 226, } },
|
||||
{ 652800, { }, { 1042425, 8144, -940, 0, 0, 226, } },
|
||||
{ 691200, { }, { 1042425, 8144, -940, 0, 0, 226, } },
|
||||
{ 729600, { }, { 1066317, 8144, -940, 0, 0, 226, } },
|
||||
{ 768000, { }, { 1066317, 8144, -940, 0, 0, 226, } },
|
||||
{ 806400, { }, { 1093208, 8144, -940, 0, 0, 226, } },
|
||||
{ 844800, { }, { 1093208, 8144, -940, 0, 0, 226, } },
|
||||
{ 883200, { }, { 1118100, 8144, -940, 0, 0, 226, } },
|
||||
{ 921600, { }, { 1118100, 8144, -940, 0, 0, 226, } },
|
||||
{ 960000, { }, { 1156991, 8144, -940, 0, 0, 226, } },
|
||||
{ 998400, { }, { 1156991, 8144, -940, 0, 0, 226, } },
|
||||
{ 1036800, { }, { } },
|
||||
{ 1075200, { }, { } },
|
||||
// { 1152000, { }, { } },
|
||||
{ 1152000, { }, { } },
|
||||
{ 1228800, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1267200, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1344000, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1382400, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1420800, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1459200, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1497600, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1536000, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
},
|
||||
|
||||
.marikoGpuDvfsTable = {
|
||||
@@ -464,9 +440,9 @@ volatile CustomizeTable C = {
|
||||
{ 921600, { }, { 970060,-10108, -614,-179, 1508, -13 } },
|
||||
{ 998400, { }, { 1065665,-16075, -497,-179, 3213, 9 } },
|
||||
{ 1075200, { }, { 1132576,-16093, -648, 0, 1077, 40 } },
|
||||
// { 1152000, { }, { 1180029,-14534, -830, 0, 1469, 110 } },
|
||||
// { 1228800, { }, { 1248293,-16383, -859, 0, 3722, 313 } },
|
||||
// { 1267200, { }, { 1286399,-17475, -867, 0, 3681, 559 } },
|
||||
{ 1152000, { }, { 1180029,-14534, -830, 0, 1469, 110 } },
|
||||
{ 1228800, { }, { 1248293,-16383, -859, 0, 3722, 313 } },
|
||||
{ 1267200, { }, { 1286399,-17475, -867, 0, 3681, 559 } },
|
||||
},
|
||||
|
||||
.marikoGpuDvfsTableSLT = {
|
||||
@@ -486,7 +462,7 @@ volatile CustomizeTable C = {
|
||||
{ 1075200, { }, { 1132576, -16093, -648, 0, 1077, 40 } },
|
||||
{ 1152000, { }, { 1180029, -14534, -830, 0, 1469, 110 } },
|
||||
{ 1228800, { }, { 1238293, -16383, -859, 0, 3722, 313 } },
|
||||
// { 1267200, { }, { 1276399, -17475, -867, 0, 3681, 559 } },
|
||||
{ 1267200, { }, { 1276399, -17475, -867, 0, 3681, 559 } },
|
||||
},
|
||||
|
||||
.marikoGpuDvfsTableHiOPT = {
|
||||
@@ -514,6 +490,13 @@ volatile CustomizeTable C = {
|
||||
{ 1459200, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1497600, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1536000, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1574400, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1612800, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1651200, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1689600, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1728000, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1766400, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
{ 1804800, { }, { 0, 0, 0, 0, 0, 0 } },
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -125,8 +125,8 @@ typedef struct CustomizeTable {
|
||||
|
||||
u32 gpuSpeedo;
|
||||
|
||||
u32 eristaGpuVoltArray[27];
|
||||
u32 marikoGpuVoltArray[24];
|
||||
u32 eristaGpuVoltArray[24];
|
||||
u32 marikoGpuVoltArray[31];
|
||||
|
||||
u32 fineTune_t6_tRTW;
|
||||
u32 fineTune_t7_tWTR;
|
||||
|
||||
@@ -99,25 +99,4 @@ namespace ams::ldr::hoc {
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
namespace panic {
|
||||
/* Requires modifying g_ams_handlers in secmon_smc_handler.cpp */
|
||||
constexpr inline void SmcError(u32 rgb) {
|
||||
SecmonArgs args = {};
|
||||
constexpr u32 SmcShowErrorID = 0xF0000005;
|
||||
args.X[0] = SmcShowErrorID;
|
||||
args.X[1] = rgb;
|
||||
svcCallSecureMonitor(&args);
|
||||
}
|
||||
|
||||
constexpr inline u32 PackCode(u32 r, u32 g, u32 b) {
|
||||
return ((r & 0xF) << 8) | ((g & 0xF) << 4) | ((b & 0xF) << 0);
|
||||
}
|
||||
|
||||
constexpr u32 Gpu = PackCode(0xF, 0x7, 0x0);
|
||||
constexpr u32 Cpu = PackCode(0xF, 0x0, 0x0);
|
||||
constexpr u32 Emc = PackCode(0x0, 0xF, 0xF);
|
||||
constexpr u32 Patch = PackCode(0x8, 0x0, 0xF);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,163 +22,153 @@
|
||||
|
||||
namespace ams::ldr::hoc::pcv {
|
||||
|
||||
Result MemFreqPllmLimit(u32* ptr) {
|
||||
clk_pll_param* entry = reinterpret_cast<clk_pll_param *>(ptr);
|
||||
R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry());
|
||||
Result MemFreqPllmLimit(u32* ptr) {
|
||||
clk_pll_param* entry = reinterpret_cast<clk_pll_param *>(ptr);
|
||||
R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry());
|
||||
|
||||
// Double the max clk simply
|
||||
u32 max_clk = entry->freq * 2;
|
||||
entry->freq = max_clk;
|
||||
entry->vco_max = max_clk;
|
||||
// Double the max clk simply
|
||||
u32 max_clk = entry->freq * 2;
|
||||
entry->freq = max_clk;
|
||||
entry->vco_max = max_clk;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MemVoltHandler(u32* ptr) {
|
||||
// ptr value might be default_uv or max_uv
|
||||
regulator* entries[2] = {
|
||||
reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_1.default_uv)),
|
||||
reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_1.max_uv)),
|
||||
};
|
||||
|
||||
constexpr u32 uv_step = 12'500;
|
||||
constexpr u32 uv_min = 600'000;
|
||||
|
||||
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());
|
||||
R_UNLESS(entry->type_1.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
|
||||
R_UNLESS(entry->type_1.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
|
||||
R_SUCCEED();
|
||||
};
|
||||
|
||||
regulator* entry = nullptr;
|
||||
for (auto& i : entries) {
|
||||
if (R_SUCCEEDED(validator(i)))
|
||||
entry = i;
|
||||
}
|
||||
|
||||
Result MemVoltHandler(u32* ptr) {
|
||||
// ptr value might be default_uv or max_uv
|
||||
regulator* entries[2] = {
|
||||
reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_1.default_uv)),
|
||||
reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_1.max_uv)),
|
||||
};
|
||||
R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry());
|
||||
|
||||
constexpr u32 uv_step = 12'500;
|
||||
constexpr u32 uv_min = 600'000;
|
||||
u32 emc_uv = C.commonEmcMemVolt;
|
||||
if (!emc_uv)
|
||||
R_SKIP();
|
||||
|
||||
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());
|
||||
R_UNLESS(entry->type_1.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
|
||||
R_UNLESS(entry->type_1.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
|
||||
R_SUCCEED();
|
||||
};
|
||||
if (emc_uv % uv_step)
|
||||
emc_uv = emc_uv / uv_step * uv_step; // rounding
|
||||
|
||||
regulator* entry = nullptr;
|
||||
for (auto& i : entries) {
|
||||
if (R_SUCCEEDED(validator(i))) {
|
||||
entry = i;
|
||||
}
|
||||
}
|
||||
PATCH_OFFSET(ptr, emc_uv);
|
||||
|
||||
R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u32 emc_uv = C.commonEmcMemVolt;
|
||||
if (!emc_uv) {
|
||||
R_SKIP();
|
||||
}
|
||||
void SafetyCheck() {
|
||||
// if (C.custRev != CUST_REV)
|
||||
// CRASH("Triggered");
|
||||
|
||||
if (emc_uv % uv_step) {
|
||||
emc_uv = emc_uv / uv_step * uv_step; // rounding
|
||||
}
|
||||
|
||||
PATCH_OFFSET(ptr, emc_uv);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SafetyCheck() {
|
||||
// if (C.custRev != CUST_REV)
|
||||
// CRASH("Triggered");
|
||||
|
||||
struct sValidator {
|
||||
volatile u32 value;
|
||||
u32 min;
|
||||
u32 max;
|
||||
bool value_required = false;
|
||||
u32 panic;
|
||||
|
||||
Result check() {
|
||||
if (!value_required && !value)
|
||||
R_SUCCEED();
|
||||
|
||||
if (min && value < min)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
if (max && value > max)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
struct sValidator {
|
||||
volatile u32 value;
|
||||
u32 min;
|
||||
u32 max;
|
||||
bool value_required = false;
|
||||
|
||||
Result check() {
|
||||
if (!value_required && !value)
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
|
||||
u32 marikoCpuDvfsMaxFreq;
|
||||
if (C.marikoCpuUVHigh) {
|
||||
marikoCpuDvfsMaxFreq = static_cast<u32>(
|
||||
GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq
|
||||
);
|
||||
} else {
|
||||
marikoCpuDvfsMaxFreq = static_cast<u32>(
|
||||
GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq
|
||||
);
|
||||
}
|
||||
u32 eristaGpuDvfsMaxFreq;
|
||||
switch (C.eristaGpuUV) {
|
||||
if (min && value < min)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
if (max && value > max)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
|
||||
u32 marikoCpuDvfsMaxFreq;
|
||||
if (C.marikoCpuUVHigh) {
|
||||
marikoCpuDvfsMaxFreq = static_cast<u32>(
|
||||
GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq
|
||||
);
|
||||
} else {
|
||||
marikoCpuDvfsMaxFreq = static_cast<u32>(
|
||||
GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq
|
||||
);
|
||||
}
|
||||
u32 eristaGpuDvfsMaxFreq;
|
||||
switch (C.eristaGpuUV)
|
||||
{
|
||||
case 0:
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
|
||||
break;
|
||||
case 1:
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq);
|
||||
break;
|
||||
case 2:
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq);
|
||||
break;
|
||||
default:
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 marikoGpuDvfsMaxFreq;
|
||||
switch (C.marikoGpuUV) {
|
||||
case 0:
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
||||
break;
|
||||
case 1:
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq);
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq);
|
||||
break;
|
||||
case 2:
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq);
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq);
|
||||
break;
|
||||
default:
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 marikoGpuDvfsMaxFreq;
|
||||
switch (C.marikoGpuUV) {
|
||||
case 0:
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
||||
break;
|
||||
case 1:
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq);
|
||||
break;
|
||||
case 2:
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq);
|
||||
break;
|
||||
default:
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
||||
break;
|
||||
}
|
||||
|
||||
using namespace ams::ldr::hoc::pcv;
|
||||
sValidator validators[] = {
|
||||
{ C.eristaCpuBoostClock, 1020'000, 2397'000, true, panic::Cpu },
|
||||
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true, panic::Cpu },
|
||||
{ C.eristaCpuMaxVolt, 1000, 1260, false, panic::Cpu },
|
||||
{ C.marikoCpuMaxVolt, 1000, 1200, false, panic::Cpu },
|
||||
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, false, panic::Cpu },
|
||||
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000, false, panic::Cpu },
|
||||
{ C.commonEmcMemVolt, 912'500, 1350'000, false, panic::Emc }, // Official burst vmax for the RAMs is 1500mV
|
||||
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000, false, panic::Emc },
|
||||
{ C.marikoEmcMaxClock, 1600'000, 3500'000, false, panic::Emc },
|
||||
{ C.marikoEmcVddqVolt, 250'000, 700'000, false, panic::Emc },
|
||||
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, false, panic::Gpu },
|
||||
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000, false, panic::Gpu },
|
||||
{ C.marikoGpuVmax, 800, 960, false, panic::Gpu },
|
||||
};
|
||||
|
||||
for (auto &v : validators) {
|
||||
if (R_FAILED(v.check())) {
|
||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||
panic::SmcError(v.panic);
|
||||
#endif
|
||||
|
||||
CRASH("Validation FAIL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
SafetyCheck();
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
if (isMariko)
|
||||
mariko::Patch(mapped_nso, nso_size);
|
||||
else
|
||||
erista::Patch(mapped_nso, nso_size);
|
||||
#endif
|
||||
using namespace ams::ldr::hoc::pcv;
|
||||
sValidator validators[] = {
|
||||
{ C.eristaCpuBoostClock, 1020'000, 3009'000, true },
|
||||
{ C.marikoCpuBoostClock, 1020'000, 3009'000, true },
|
||||
{ C.commonEmcMemVolt, 912'500, 1500'000 }, // Official burst vmax for the RAMs is 1500mV
|
||||
{ C.eristaCpuMaxVolt, 1000, 1500 },
|
||||
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2700'000 },
|
||||
{ C.marikoCpuMaxVolt, 1000, 1500 },
|
||||
{ C.marikoEmcMaxClock, 1600'000, 3500'000 },
|
||||
{ C.marikoEmcVddqVolt, 250'000, 1500'000 },
|
||||
{ eristaCpuDvfsMaxFreq, 1785'000, 3009'000 },
|
||||
{ marikoCpuDvfsMaxFreq, 1785'000, 3009'000 },
|
||||
{ eristaGpuDvfsMaxFreq, 768'000, 1536'000 },
|
||||
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000 },
|
||||
};
|
||||
|
||||
for (auto& i : validators) {
|
||||
if (R_FAILED(i.check()))
|
||||
CRASH("Validation FAIL");
|
||||
}
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
SafetyCheck();
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
if (isMariko)
|
||||
mariko::Patch(mapped_nso, nso_size);
|
||||
else
|
||||
erista::Patch(mapped_nso, nso_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace ams::ldr::hoc::pcv {
|
||||
|
||||
static const u32 cpuVoltThermalData[] = { 620, 1120, 20000, 620, 1120, 70000, 950, 1132, 0, 950, 1227, 0 };
|
||||
|
||||
static const u32 allowedCpuMaxFrequencies[] = { 1'963'500, 2'091'000, 2'193'000, 2'295'000, 2'397'000, 2'499'000, 2'601'000, 2'703'000, };
|
||||
static const u32 allowedCpuMaxFrequencies[] = { 1'963'000, 2'091'000, 2'193'000, 2'295'000, 2'397'000, 2'499'000, 2'601'000, 2'703'000, 2'805'000, 2'907'000, 3'009'000, };
|
||||
|
||||
constexpr cvb_entry_t GpuCvbTableDefault[] = {
|
||||
// GPUB01_NA_CVB_TABLE
|
||||
@@ -90,6 +90,38 @@ namespace ams::ldr::hoc::pcv {
|
||||
static const u32 gpuVoltThermalPattern[] = { 800, 1120, 0, 610, 1120, 20000, 610, 1120, 30000, 610, 1120, 50000, 610, 1120, 70000, 610, 1120, 90000, };
|
||||
static_assert(sizeof(gpuVoltThermalPattern) == 72, "Invalid gpuVoltThermalPattern");
|
||||
|
||||
struct SpeedoVminTable {
|
||||
u32 speedo;
|
||||
u32 voltage;
|
||||
};
|
||||
|
||||
struct RamVminOffsetTable {
|
||||
u32 maxClock;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
static const SpeedoVminTable vminTable[] {
|
||||
{1400, 610}, // LOW SPEEDO -> use stock vmin
|
||||
{1560, 590},
|
||||
{1583, 570},
|
||||
{1620, 565},
|
||||
{1670, 560},
|
||||
{1694, 555},
|
||||
{1731, 550},
|
||||
{1750, 540},
|
||||
{0xFFFFFFFF, 530},
|
||||
};
|
||||
|
||||
static const RamVminOffsetTable ramOffset[] {
|
||||
{2400000, 5},
|
||||
{2533000, 10},
|
||||
{2666000, 15},
|
||||
{2800000, 20},
|
||||
{2933000, 25},
|
||||
{3200000, 30},
|
||||
{0xFFFFFFFF, 35},
|
||||
};
|
||||
|
||||
/* GPU Max Clock asm Pattern:
|
||||
*
|
||||
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
|
||||
@@ -170,14 +202,20 @@ namespace ams::ldr::hoc::pcv {
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 CpuVoltOfficial = 1227;
|
||||
constexpr u32 CpuVoltOfficial = 1235;
|
||||
|
||||
constexpr u32 CpuVminOfficial = 825;
|
||||
|
||||
constexpr u32 CpuVoltL4T = 1257'000;
|
||||
constexpr u32 CpuVoltL4T = 1235'000;
|
||||
constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 };
|
||||
|
||||
inline bool CpuMaxVoltPatternFn(u32* ptr32) {
|
||||
u32 val = *ptr32;
|
||||
return (val == 1132 || val == 1170 || val == 1227);
|
||||
}
|
||||
static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 };
|
||||
static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "invalid cpuVoltDvfsPattern size");
|
||||
static const u32 cpuVoltDvfsOffsets[] = { 5, 6, 7, 8, 9 };
|
||||
static_assert(sizeof(cpuVoltDvfsPattern) == sizeof(cpuVoltDvfsOffsets), "Invalid cpuVoltDvfsPattern");
|
||||
|
||||
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");
|
||||
@@ -185,19 +223,13 @@ namespace ams::ldr::hoc::pcv {
|
||||
constexpr u32 GpuClkPllMax = 921'600'000;
|
||||
constexpr u32 GpuVminOfficial = 810;
|
||||
|
||||
constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 };
|
||||
|
||||
inline bool CpuMaxVoltPatternFn(u32* ptr32) {
|
||||
u32 val = *ptr32;
|
||||
return (val == 1132 || val == 1170 || val == 1227);
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -252,7 +284,6 @@ namespace ams::ldr::hoc::pcv {
|
||||
};
|
||||
|
||||
constexpr u32 MemVoltHOS = 1125'000;
|
||||
constexpr u32 EmcClkMinFreq = 40800; /* 40.8 MHz table only exists on erista. */
|
||||
constexpr u32 EmcClkPllmLimit = 1866'000'000;
|
||||
|
||||
constexpr u32 MTC_TABLE_REV = 7;
|
||||
|
||||
@@ -16,154 +16,153 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
namespace ams::ldr::hoc::pcv {
|
||||
namespace ams::ldr::hoc::pcv {
|
||||
|
||||
typedef struct cvb_coefficients {
|
||||
s32 c0 = 0;
|
||||
s32 c1 = 0;
|
||||
s32 c2 = 0;
|
||||
s32 c3 = 0;
|
||||
s32 c4 = 0;
|
||||
s32 c5 = 0;
|
||||
} cvb_coefficients;
|
||||
typedef struct cvb_coefficients {
|
||||
s32 c0 = 0;
|
||||
s32 c1 = 0;
|
||||
s32 c2 = 0;
|
||||
s32 c3 = 0;
|
||||
s32 c4 = 0;
|
||||
s32 c5 = 0;
|
||||
} cvb_coefficients;
|
||||
|
||||
typedef struct cvb_entry_t {
|
||||
u64 freq;
|
||||
cvb_coefficients cvb_dfll_param;
|
||||
cvb_coefficients cvb_pll_param;
|
||||
} cvb_entry_t;
|
||||
static_assert(sizeof(cvb_entry_t) == 0x38);
|
||||
typedef struct cvb_entry_t {
|
||||
u64 freq;
|
||||
cvb_coefficients cvb_dfll_param;
|
||||
cvb_coefficients cvb_pll_param;
|
||||
} cvb_entry_t;
|
||||
static_assert(sizeof(cvb_entry_t) == 0x38);
|
||||
|
||||
typedef struct cvb_cpu_dfll_data {
|
||||
u32 tune0_low;
|
||||
u32 tune0_high;
|
||||
u32 tune1_low;
|
||||
u32 tune1_high;
|
||||
unsigned int tune_high_min_millivolts;
|
||||
unsigned int tune_high_margin_millivolts;
|
||||
unsigned long dvco_calibration_max;
|
||||
} cvb_cpu_dfll_data;
|
||||
typedef struct cvb_cpu_dfll_data {
|
||||
u32 tune0_low;
|
||||
u32 tune0_high;
|
||||
u32 tune1_low;
|
||||
u32 tune1_high;
|
||||
unsigned int tune_high_min_millivolts;
|
||||
unsigned int tune_high_margin_millivolts;
|
||||
unsigned long dvco_calibration_max;
|
||||
} cvb_cpu_dfll_data;
|
||||
|
||||
typedef struct emc_dvb_dvfs_table_t {
|
||||
u64 freq;
|
||||
s32 volt[4] = {0};
|
||||
} emc_dvb_dvfs_table_t;
|
||||
typedef struct emc_dvb_dvfs_table_t {
|
||||
u64 freq;
|
||||
s32 volt[4] = {0};
|
||||
} emc_dvb_dvfs_table_t;
|
||||
|
||||
typedef struct __attribute__((packed)) div_nmp {
|
||||
u8 divn_shift;
|
||||
u8 divn_width;
|
||||
u8 divm_shift;
|
||||
u8 divm_width;
|
||||
u8 divp_shift;
|
||||
u8 divp_width;
|
||||
u8 override_divn_shift;
|
||||
u8 override_divm_shift;
|
||||
u8 override_divp_shift;
|
||||
} div_nmp;
|
||||
typedef struct __attribute__((packed)) div_nmp {
|
||||
u8 divn_shift;
|
||||
u8 divn_width;
|
||||
u8 divm_shift;
|
||||
u8 divm_width;
|
||||
u8 divp_shift;
|
||||
u8 divp_width;
|
||||
u8 override_divn_shift;
|
||||
u8 override_divm_shift;
|
||||
u8 override_divp_shift;
|
||||
} div_nmp;
|
||||
|
||||
typedef struct __attribute__((packed)) clk_pll_param {
|
||||
u32 freq;
|
||||
u32 input_min;
|
||||
u32 input_max;
|
||||
u32 cf_min;
|
||||
u32 cf_max;
|
||||
u32 vco_min;
|
||||
u32 vco_max;
|
||||
s32 lock_delay;
|
||||
u32 fixed_rate;
|
||||
u32 unk_0;
|
||||
struct div_nmp *div_nmp;
|
||||
u32 unk_1[4];
|
||||
void (*unk_fn)(u64* unk_struct); // set_defaults?
|
||||
} clk_pll_param;
|
||||
typedef struct __attribute__((packed)) clk_pll_param {
|
||||
u32 freq;
|
||||
u32 input_min;
|
||||
u32 input_max;
|
||||
u32 cf_min;
|
||||
u32 cf_max;
|
||||
u32 vco_min;
|
||||
u32 vco_max;
|
||||
s32 lock_delay;
|
||||
u32 fixed_rate;
|
||||
u32 unk_0;
|
||||
struct div_nmp *div_nmp;
|
||||
u32 unk_1[4];
|
||||
void (*unk_fn)(u64* unk_struct); // set_defaults?
|
||||
} clk_pll_param;
|
||||
|
||||
typedef struct __attribute__((packed)) dvfs_rail {
|
||||
u32 id;
|
||||
u32 unk_0[5];
|
||||
u32 freq;
|
||||
u32 unk_1[8];
|
||||
u32 unk_flag;
|
||||
u32 min_mv;
|
||||
u32 step_mv;
|
||||
u32 max_mv;
|
||||
u32 unk_2[11];
|
||||
} dvfs_rail;
|
||||
typedef struct __attribute__((packed)) dvfs_rail {
|
||||
u32 id;
|
||||
u32 unk_0[5];
|
||||
u32 freq;
|
||||
u32 unk_1[8];
|
||||
u32 unk_flag;
|
||||
u32 min_mv;
|
||||
u32 step_mv;
|
||||
u32 max_mv;
|
||||
u32 unk_2[11];
|
||||
} dvfs_rail;
|
||||
|
||||
typedef struct __attribute__((packed)) regulator {
|
||||
u64 id;
|
||||
const char* name;
|
||||
u32 type;
|
||||
union {
|
||||
struct {
|
||||
u32 volt_reg;
|
||||
u32 step_uv;
|
||||
u32 min_uv;
|
||||
u32 default_uv;
|
||||
u32 max_uv;
|
||||
u32 unk_0[2];
|
||||
} type_1;
|
||||
struct {
|
||||
u32 unk_0;
|
||||
u32 step_uv;
|
||||
u32 unk_1;
|
||||
u32 min_uv;
|
||||
u32 max_uv;
|
||||
u32 unk_2;
|
||||
u32 default_uv;
|
||||
} type_2_3;
|
||||
};
|
||||
u32 unk_x[60];
|
||||
} regulator;
|
||||
static_assert(sizeof(regulator) == 0x120);
|
||||
typedef struct __attribute__((packed)) regulator {
|
||||
u64 id;
|
||||
const char* name;
|
||||
u32 type;
|
||||
union {
|
||||
struct {
|
||||
u32 volt_reg;
|
||||
u32 step_uv;
|
||||
u32 min_uv;
|
||||
u32 default_uv;
|
||||
u32 max_uv;
|
||||
u32 unk_0[2];
|
||||
} type_1;
|
||||
struct {
|
||||
u32 unk_0;
|
||||
u32 step_uv;
|
||||
u32 unk_1;
|
||||
u32 min_uv;
|
||||
u32 max_uv;
|
||||
u32 unk_2;
|
||||
u32 default_uv;
|
||||
} type_2_3;
|
||||
};
|
||||
u32 unk_x[60];
|
||||
} regulator;
|
||||
static_assert(sizeof(regulator) == 0x120);
|
||||
|
||||
constexpr u32 CpuClkOSLimit = 1785'000;
|
||||
constexpr u32 CpuClkOSLimit = 1785'000;
|
||||
|
||||
constexpr u32 EmcClkOSLimit = 1600'000;
|
||||
constexpr u32 EmcClkOSLimit = 1600'000;
|
||||
|
||||
#define R_SKIP() R_SUCCEED()
|
||||
#define R_SKIP() R_SUCCEED()
|
||||
|
||||
// Count 32 / Index 31 is reserved to be empty
|
||||
constexpr size_t DvfsTableEntryCount = 32;
|
||||
constexpr size_t DvfsTableEntryLimit = DvfsTableEntryCount - 1;
|
||||
// Count 32 / Index 31 is reserved to be empty
|
||||
constexpr size_t DvfsTableEntryCount = 32;
|
||||
constexpr size_t DvfsTableEntryLimit = DvfsTableEntryCount - 1;
|
||||
|
||||
template<typename T>
|
||||
size_t GetDvfsTableEntryCount(T* table_head) {
|
||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||
template<typename T>
|
||||
size_t GetDvfsTableEntryCount(T* table_head) {
|
||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||
|
||||
auto is_empty = [](NT* entry) {
|
||||
uint8_t* m = reinterpret_cast<uint8_t *>(entry);
|
||||
for (size_t i = 0; i < sizeof(NT); i++) {
|
||||
if (*(m + i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto is_empty = [](NT* entry) {
|
||||
uint8_t* m = reinterpret_cast<uint8_t *>(entry);
|
||||
for (size_t i = 0; i < sizeof(NT); i++) {
|
||||
if (*(m + i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
NT* table = const_cast<NT *>(table_head);
|
||||
size_t count = 0;
|
||||
while (count < DvfsTableEntryLimit) {
|
||||
if (is_empty(table++)) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return DvfsTableEntryLimit;
|
||||
}
|
||||
NT* table = const_cast<NT *>(table_head);
|
||||
size_t count = 0;
|
||||
while (count < DvfsTableEntryLimit) {
|
||||
if (is_empty(table++)) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return DvfsTableEntryLimit;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* GetDvfsTableLastEntry(T* table_head) {
|
||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||
template<typename T>
|
||||
T* GetDvfsTableLastEntry(T* table_head) {
|
||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||
|
||||
NT* table = const_cast<NT *>(table_head);
|
||||
size_t count = GetDvfsTableEntryCount(table_head);
|
||||
if (!count) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t index = count - 1;
|
||||
return table + index;
|
||||
}
|
||||
NT* table = const_cast<NT *>(table_head);
|
||||
size_t count = GetDvfsTableEntryCount(table_head);
|
||||
if (!count) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t index = count - 1;
|
||||
return table + index;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,23 +25,23 @@
|
||||
namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
Result CpuVoltDvfs(u32 *ptr) {
|
||||
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
|
||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||
if (MatchesPattern(ptr, cpuVoltDvfsPattern, cpuVoltDvfsOffsets)) {
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||
}
|
||||
|
||||
Result CpuVoltThermals(u32 *ptr) {
|
||||
@@ -52,7 +52,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
if (C.eristaCpuVmin) {
|
||||
PATCH_OFFSET( ptr, C.eristaCpuVmin);
|
||||
PATCH_OFFSET(ptr + 3, C.eristaCpuVmin);
|
||||
PATCH_OFFSET(ptr + 6, C.eristaCpuVmin);
|
||||
PATCH_OFFSET(ptr + 9, C.eristaCpuVmin);
|
||||
}
|
||||
|
||||
if (C.eristaCpuMaxVolt) {
|
||||
@@ -67,12 +67,12 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
Result CpuVoltDfll(u32* ptr) {
|
||||
cvb_cpu_dfll_data *entry = reinterpret_cast<cvb_cpu_dfll_data *>(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());
|
||||
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) {
|
||||
if( !C.eristaCpuUV) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
@@ -104,19 +104,20 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
}
|
||||
|
||||
Result GpuVoltDVFS(u32 *ptr) {
|
||||
if (std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern))) {
|
||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||
}
|
||||
u32 result = std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern));
|
||||
|
||||
if (C.eristaGpuVmin) {
|
||||
if (result)
|
||||
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))) {
|
||||
u32 result = std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern));
|
||||
if (result) {
|
||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||
}
|
||||
|
||||
@@ -354,54 +355,44 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
table->min_volt = std::min(static_cast<u32>(1050), 900 + C.emcDvbShift * 25);
|
||||
}
|
||||
|
||||
/* Probably more intuitive to point to 40800 rather than 1600000, but oh well. */
|
||||
Result MemFreqMtcTable(u32 *ptr) {
|
||||
if (GET_MAX_OF_ARR(maxEmcClocks) <= EmcClkOSLimit) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
u32 khz_list[] = { 40800, 68000, 102000, 204000, 408000, 665600, 800000, 1065600, 1331200, 1600000 };
|
||||
std::sort(maxEmcClocks, maxEmcClocks + std::size(maxEmcClocks));
|
||||
u32 khz_list_size = std::size(khz_list);
|
||||
u32 khz_list[] = {1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800};
|
||||
std::sort(maxEmcClocks, maxEmcClocks + std::size(maxEmcClocks), std::greater<>());
|
||||
u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
|
||||
|
||||
// Generate list for mtc table pointers
|
||||
EristaMtcTable *table_list[khz_list_size];
|
||||
for (u32 i = 0; i < khz_list_size; i++) {
|
||||
u32 mtcIndex = khz_list_size - 1 - i;
|
||||
u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable);
|
||||
table_list[mtcIndex] = reinterpret_cast<EristaMtcTable *>(table);
|
||||
R_UNLESS(table_list[mtcIndex]->rate_khz == khz_list[mtcIndex], ldr::ResultInvalidMtcTable());
|
||||
R_UNLESS(table_list[mtcIndex]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
|
||||
table_list[i] = reinterpret_cast<EristaMtcTable *>(table);
|
||||
R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
|
||||
R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
|
||||
}
|
||||
|
||||
/* If we oc ram at all, tables are always shifted by at least 1. */
|
||||
u32 tableShifts = 1;
|
||||
for (u32 i = 0; i < std::size(maxEmcClocks) - 1; ++i) {
|
||||
/* Duplicated mtc tables may cause pcv to not select frequencies properly, causing issues. */
|
||||
if (maxEmcClocks[i] != maxEmcClocks[i + 1] && maxEmcClocks[i] > EmcClkOSLimit) {
|
||||
++tableShifts;
|
||||
u32 additionalFreqs = 0;
|
||||
for (u32 i = 0; i < std::size(maxEmcClocks); ++i) {
|
||||
if (maxEmcClocks[i] > EmcClkOSLimit) {
|
||||
++additionalFreqs;
|
||||
} else {
|
||||
maxEmcClocks[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Erista has extra, useless mtc tables, such as 40.8 Mhz, overwrite them to make room for oc freqs. */
|
||||
/* More than 3 tables can be overwritten, but 3 is plenty. */
|
||||
std::memmove(table_list[0], table_list[tableShifts], sizeof(EristaMtcTable) * (khz_list_size - tableShifts));
|
||||
|
||||
/* Since we're not scaling r/w latency properly on Erista, we first overwrite the tables with the 1600 MHz table before scaling it. */
|
||||
for (u32 i = 0; i < tableShifts; ++i) {
|
||||
std::memcpy(table_list[khz_list_size - i - 1], table_list[khz_list_size - tableShifts - 1], sizeof(EristaMtcTable));
|
||||
// Make room for new mtc table, discarding useless 40.8, 68000 and 102000 MHz table
|
||||
// 40800 overwritten by 204000, ..., 1331200 overwritten by 1600000, leaving table_list[0], table_list[1] and table_list[2] not overwritten
|
||||
for (u32 i = khz_list_size - 1; i > additionalFreqs - 1; --i) {
|
||||
std::memcpy(static_cast<void *>(table_list[i]), static_cast<void *>(table_list[i - additionalFreqs]), sizeof(EristaMtcTable));
|
||||
}
|
||||
|
||||
for (u32 i = tableShifts, j = 0; i > 0 && j < std::size(maxEmcClocks); ++j) {
|
||||
if (!maxEmcClocks[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
table_list[khz_list_size - i]->rate_khz = maxEmcClocks[j];
|
||||
MemMtcTableAutoAdjust(table_list[khz_list_size - i]);
|
||||
--i;
|
||||
for (u32 i = 0; i < additionalFreqs; ++i) {
|
||||
/* Since we're not scaling latency timings properly, copy over the 1600Mhz table to get the closest timings. */
|
||||
std::memcpy(table_list[i], table_list[additionalFreqs], sizeof(EristaMtcTable));
|
||||
table_list[i]->rate_khz = maxEmcClocks[i];
|
||||
MemMtcTableAutoAdjust(table_list[i]);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -416,6 +407,20 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
Result CpuVoltRange(u32* ptr) {
|
||||
u32 min_volt_got = *(ptr - 1);
|
||||
for (const auto& mv : CpuMinVolts) {
|
||||
if (min_volt_got != mv)
|
||||
continue;
|
||||
|
||||
if (!C.eristaCpuMaxVolt)
|
||||
R_SKIP();
|
||||
|
||||
PATCH_OFFSET(ptr, C.eristaCpuMaxVolt);
|
||||
R_SUCCEED();
|
||||
}
|
||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||
}
|
||||
|
||||
Result GpuFreqPllMax(u32 *ptr) {
|
||||
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
|
||||
@@ -431,31 +436,31 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// patch out 1305MHz limit on erista, don't use this!
|
||||
// Result GpuFreqPllLimit(u32 *ptr) {
|
||||
// u32 prev_freq = *(ptr - 1);
|
||||
Result GpuFreqPllLimit(u32 *ptr) {
|
||||
u32 prev_freq = *(ptr - 1);
|
||||
|
||||
// if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) {
|
||||
// R_THROW(ldr::ResultInvalidGpuPllEntry());
|
||||
// }
|
||||
if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) {
|
||||
R_THROW(ldr::ResultInvalidGpuPllEntry());
|
||||
}
|
||||
|
||||
// PATCH_OFFSET(ptr, 3600000);
|
||||
PATCH_OFFSET(ptr, 3600000);
|
||||
|
||||
// R_SUCCEED();
|
||||
// }
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
PatcherEntry<u32> patches[] = {
|
||||
{"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq)},
|
||||
{"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, CpuVminOfficial},
|
||||
{"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial},
|
||||
{"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0xFFEAD0FF},
|
||||
{"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial},
|
||||
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial},
|
||||
{"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, 825},
|
||||
{"CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn},
|
||||
{"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, 825},
|
||||
{"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0xFFEAD0FF},
|
||||
{"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, 810},
|
||||
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, 810},
|
||||
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq)},
|
||||
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn},
|
||||
{"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax},
|
||||
// {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit},
|
||||
{"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit},
|
||||
{"MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit},
|
||||
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit},
|
||||
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit},
|
||||
@@ -474,10 +479,6 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
for (auto &entry : patches) {
|
||||
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
||||
if (R_FAILED(entry.CheckResult())) {
|
||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||
panic::SmcError(panic::Patch);
|
||||
#endif
|
||||
|
||||
CRASH(entry.description);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,46 @@
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
u32 GetGpuVminVoltage() {
|
||||
for (auto e : vminTable) {
|
||||
if (C.gpuSpeedo <= e.speedo) {
|
||||
return e.voltage;
|
||||
}
|
||||
}
|
||||
|
||||
return 530;
|
||||
}
|
||||
|
||||
u32 GetRamVminAdjustment(u32 vmin) {
|
||||
if (C.marikoEmcMaxClock < 2133000) {
|
||||
return vmin;
|
||||
}
|
||||
|
||||
const u32 ramScale = (((C.marikoEmcMaxClock / 1000) - 2133) / 33) * 5 + vmin;
|
||||
|
||||
for (auto r : ramOffset) {
|
||||
if (C.marikoEmcMaxClock < r.maxClock) {
|
||||
return ramScale + r.offset;
|
||||
}
|
||||
}
|
||||
|
||||
return ramScale;
|
||||
}
|
||||
|
||||
/* Note: EOS (probably?) has a bug in this function that always results in high vmin, this is fixed here. */
|
||||
u32 GetAutoVoltage() {
|
||||
u32 voltage = GetGpuVminVoltage();
|
||||
voltage = GetRamVminAdjustment(voltage);
|
||||
|
||||
u32 voltageOffset = 590 - C.commonGpuVoltOffset;
|
||||
|
||||
if (voltageOffset < voltage) {
|
||||
voltage = voltageOffset;
|
||||
}
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
||||
Result GpuVoltDVFS(u32 *ptr) {
|
||||
/* Check for valid pattern. */
|
||||
for (size_t i = 0; i < std::size(gpuDVFSPattern); ++i) {
|
||||
@@ -37,10 +77,15 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
PATCH_OFFSET(ptr + 1, C.marikoGpuVmax);
|
||||
}
|
||||
|
||||
/* C.marikoGpuVmin is non zero, user sets manual voltage. */
|
||||
if (C.marikoGpuVmin) {
|
||||
PATCH_OFFSET(ptr, C.marikoGpuVmin);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* C.marikoGpuVmin is zero, auto voltage is applied. */
|
||||
u32 autoVmin = GetAutoVoltage();
|
||||
PATCH_OFFSET(ptr, autoVmin);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -49,15 +94,24 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||
}
|
||||
|
||||
u32 vmin = C.marikoGpuVmin;
|
||||
|
||||
/* Automatic voltage. */
|
||||
if (!C.marikoGpuVmin) {
|
||||
R_SKIP();
|
||||
vmin = GetAutoVoltage();
|
||||
PATCH_OFFSET(ptr, vmin);
|
||||
PATCH_OFFSET(ptr + 3, vmin);
|
||||
PATCH_OFFSET(ptr + 6, vmin);
|
||||
PATCH_OFFSET(ptr + 9, vmin);
|
||||
} else {
|
||||
/* Manual voltage. */
|
||||
PATCH_OFFSET(ptr, vmin);
|
||||
PATCH_OFFSET(ptr + 3, vmin);
|
||||
PATCH_OFFSET(ptr + 6, vmin);
|
||||
PATCH_OFFSET(ptr + 9, vmin);
|
||||
}
|
||||
|
||||
PATCH_OFFSET(ptr + 0, 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);
|
||||
PATCH_OFFSET(ptr + 12, vmin);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -361,8 +415,6 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
u32 trefbw = refresh_raw + 0x40;
|
||||
trefbw = MIN(trefbw, static_cast<u32>(0x3FFF));
|
||||
|
||||
const u32 dyn_self_ref_control = (static_cast<u32>(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000);
|
||||
|
||||
CalculateTimings();
|
||||
|
||||
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
|
||||
@@ -394,6 +446,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
WRITE_PARAM_ALL_REG(table, emc_refresh, refresh_raw);
|
||||
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, refresh_raw / 4);
|
||||
WRITE_PARAM_ALL_REG(table, emc_trefbw, trefbw);
|
||||
const u32 dyn_self_ref_control = (static_cast<u32>(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000);
|
||||
WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, dyn_self_ref_control);
|
||||
WRITE_PARAM_ALL_REG(table, emc_pdex2wr, pdex2rw);
|
||||
WRITE_PARAM_ALL_REG(table, emc_pdex2rd, pdex2rw);
|
||||
@@ -426,9 +479,8 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, rdv);
|
||||
WRITE_PARAM_ALL_REG(table, emc_rdv_mask, rdv + 2);
|
||||
WRITE_PARAM_ALL_REG(table, emc_tr_rdv, rdv);
|
||||
/* TODO: Check this out again at some point. */
|
||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_2, 0x24);
|
||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_3, 0x24);
|
||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_2, 0x24)
|
||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_3, 0x24)
|
||||
|
||||
/* This needs some clean up. */
|
||||
constexpr double MC_ARB_DIV = 4.0;
|
||||
@@ -446,7 +498,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(tW2P / MC_ARB_DIV) + MC_ARB_SFA;
|
||||
|
||||
/* Two consecutive reads between two different dram modules. */
|
||||
/* Only above 1 for 8gb ram. */
|
||||
/* Only be above 1 for 8gb ram. */
|
||||
if (table->burst_mc_regs.mc_emem_arb_timing_r2r > 1) {
|
||||
table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / 4) - 1 + MC_ARB_SFA;
|
||||
}
|
||||
@@ -600,8 +652,9 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
// Copy unmodified 1600000 table to tmp
|
||||
std::memcpy(reinterpret_cast<void *>(tmp), reinterpret_cast<void *>(table_max), sizeof(MarikoMtcTable));
|
||||
// Adjust max freq mtc timing parameters with reference to 1331200 table
|
||||
/* TODO: Implement mariko */
|
||||
|
||||
/* Adjust timings properly according to the new frequency. */
|
||||
MemMtcTableAutoAdjust(table_max);
|
||||
|
||||
MemMtcPllmbDivisor(table_max);
|
||||
@@ -630,7 +683,6 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
#define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000),
|
||||
|
||||
/* TODO: More fine tuned values? */
|
||||
if (C.marikoEmcMaxClock < 1862400) {
|
||||
std::memcpy(new_start, default_end, sizeof(emc_dvb_dvfs_table_t));
|
||||
} else if (C.marikoEmcMaxClock < 2131200) {
|
||||
@@ -758,10 +810,6 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
for (auto &entry : patches) {
|
||||
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
||||
if (R_FAILED(entry.CheckResult())) {
|
||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||
panic::SmcError(panic::Patch);
|
||||
#endif
|
||||
|
||||
CRASH(entry.description);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,111 +20,116 @@
|
||||
|
||||
namespace ams::ldr::hoc::ptm {
|
||||
|
||||
Result CpuPtmBoost(perf_conf_entry* entry) {
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
#else
|
||||
bool isMariko = true;
|
||||
#endif
|
||||
Result CpuPtmBoost(perf_conf_entry* entry) {
|
||||
|
||||
if (!C.eristaCpuBoostClock || !C.marikoCpuBoostClock) {
|
||||
R_SUCCEED();
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
#else
|
||||
bool isMariko = true;
|
||||
#endif
|
||||
|
||||
if (!C.eristaCpuBoostClock || !C.marikoCpuBoostClock)
|
||||
R_SUCCEED();
|
||||
|
||||
u32 cpuPtmBoostNew = isMariko ? C.marikoCpuBoostClock * 1000 : C.eristaCpuBoostClock * 1000;
|
||||
|
||||
PATCH_OFFSET(&(entry->cpu_freq_1), cpuPtmBoostNew);
|
||||
PATCH_OFFSET(&(entry->cpu_freq_2), cpuPtmBoostNew);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MemPtm(perf_conf_entry* entry) {
|
||||
PATCH_OFFSET(&(entry->emc_freq_1), memPtmLimit);
|
||||
PATCH_OFFSET(&(entry->emc_freq_2), memPtmLimit);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool PtmEntryIsValid(perf_conf_entry* entry) {
|
||||
return (entry->cpu_freq_1 == entry->cpu_freq_2 &&
|
||||
entry->gpu_freq_1 == entry->gpu_freq_2 &&
|
||||
entry->emc_freq_1 == entry->emc_freq_2);
|
||||
}
|
||||
|
||||
bool PtmTablePatternFn(u32* ptr) {
|
||||
perf_conf_entry* entry = reinterpret_cast<perf_conf_entry *>(ptr);
|
||||
if (!PtmEntryIsValid(entry))
|
||||
return false;
|
||||
|
||||
return entry->cpu_freq_1 == cpuPtmDefault;
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
perf_conf_entry* confTable = nullptr;
|
||||
for (uintptr_t ptr = mapped_nso;
|
||||
ptr <= mapped_nso + nso_size - sizeof(perf_conf_entry) * entryCnt;
|
||||
ptr += sizeof(u32))
|
||||
{
|
||||
u32* ptr32 = reinterpret_cast<u32 *>(ptr);
|
||||
if (PtmTablePatternFn(ptr32)) {
|
||||
confTable = reinterpret_cast<perf_conf_entry *>(ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
u32 cpuPtmBoostNew = isMariko ? C.marikoCpuBoostClock * 1000 : C.eristaCpuBoostClock * 1000;
|
||||
|
||||
PATCH_OFFSET(&(entry->cpu_freq_1), cpuPtmBoostNew);
|
||||
PATCH_OFFSET(&(entry->cpu_freq_2), cpuPtmBoostNew);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MemPtm(perf_conf_entry* entry) {
|
||||
PATCH_OFFSET(&(entry->emc_freq_1), memPtmLimit);
|
||||
PATCH_OFFSET(&(entry->emc_freq_2), memPtmLimit);
|
||||
|
||||
R_SUCCEED();
|
||||
if (!confTable) {
|
||||
CRASH("confTable not found!");
|
||||
}
|
||||
|
||||
bool PtmEntryIsValid(perf_conf_entry* entry) {
|
||||
return (entry->cpu_freq_1 == entry->cpu_freq_2 && entry->gpu_freq_1 == entry->gpu_freq_2 && entry->emc_freq_1 == entry->emc_freq_2);
|
||||
}
|
||||
PatcherEntry<perf_conf_entry> cpuPtmBoostPatch = { "CPU Ptm Boost", &CpuPtmBoost, 2, };
|
||||
PatcherEntry<perf_conf_entry> memPtmPatch = { "MEM Ptm", &MemPtm, 16, };
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
#else
|
||||
bool isMariko = true;
|
||||
#endif
|
||||
|
||||
|
||||
for (u32 i = 0; i < entryCnt; i++) {
|
||||
perf_conf_entry* entry = confTable + i;
|
||||
|
||||
bool PtmTablePatternFn(u32* ptr) {
|
||||
perf_conf_entry* entry = reinterpret_cast<perf_conf_entry *>(ptr);
|
||||
if (!PtmEntryIsValid(entry)) {
|
||||
return false;
|
||||
LOGGING("@%p", &entry);
|
||||
CRASH("Invalid ptm confTable entry");
|
||||
}
|
||||
|
||||
return entry->cpu_freq_1 == cpuPtmDefault;
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
perf_conf_entry* confTable = nullptr;
|
||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(perf_conf_entry) * entryCnt; ptr += sizeof(u32)) {
|
||||
u32* ptr32 = reinterpret_cast<u32 *>(ptr);
|
||||
if (PtmTablePatternFn(ptr32)) {
|
||||
confTable = reinterpret_cast<perf_conf_entry *>(ptr);
|
||||
switch (entry->cpu_freq_1) {
|
||||
case cpuPtmBoost:
|
||||
cpuPtmBoostPatch.Apply(entry);
|
||||
break;
|
||||
}
|
||||
case cpuPtmDefault:
|
||||
case cpuPtmDevOC:
|
||||
break;
|
||||
default:
|
||||
LOGGING("%u (0x%08x) @%p", entry->cpu_freq_1, entry->conf_id, &(entry->cpu_freq_1));
|
||||
CRASH("Unknown CPU Freq");
|
||||
}
|
||||
|
||||
if (!confTable) {
|
||||
CRASH("confTable not found!");
|
||||
}
|
||||
|
||||
PatcherEntry<perf_conf_entry> cpuPtmBoostPatch = { "CPU Ptm Boost", &CpuPtmBoost, 2, };
|
||||
PatcherEntry<perf_conf_entry> memPtmPatch = { "MEM Ptm", &MemPtm, 16, };
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
#else
|
||||
bool isMariko = true;
|
||||
#endif
|
||||
|
||||
for (u32 i = 0; i < entryCnt; i++) {
|
||||
perf_conf_entry *entry = confTable + i;
|
||||
|
||||
if (!PtmEntryIsValid(entry)) {
|
||||
LOGGING("@%p", &entry);
|
||||
CRASH("Invalid ptm confTable entry");
|
||||
}
|
||||
|
||||
switch (entry->cpu_freq_1) {
|
||||
case cpuPtmBoost:
|
||||
cpuPtmBoostPatch.Apply(entry);
|
||||
break;
|
||||
case cpuPtmDefault:
|
||||
case cpuPtmDevOC:
|
||||
break;
|
||||
default:
|
||||
LOGGING("%u (0x%08x) @%p", entry->cpu_freq_1, entry->conf_id, &(entry->cpu_freq_1));
|
||||
CRASH("Unknown CPU Freq");
|
||||
}
|
||||
|
||||
switch (entry->emc_freq_1) {
|
||||
case memPtmLimit:
|
||||
case memPtmAlt:
|
||||
case memPtmClamp:
|
||||
if (isMariko) {
|
||||
memPtmPatch.Apply(entry);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGGING("%u (0x%08x) @%p", entry->emc_freq_1, entry->conf_id, &(entry->emc_freq_2));
|
||||
CRASH("Unknown MEM Freq");
|
||||
}
|
||||
}
|
||||
|
||||
LOGGING("%s Count: %zu", cpuPtmBoostPatch.description, cpuPtmBoostPatch.patched_count);
|
||||
if (R_FAILED(cpuPtmBoostPatch.CheckResult()))
|
||||
CRASH(cpuPtmBoostPatch.description);
|
||||
|
||||
if (isMariko) {
|
||||
LOGGING("%s Count: %zu", memPtmPatch.description, memPtmPatch.patched_count);
|
||||
if (R_FAILED(memPtmPatch.CheckResult()))
|
||||
CRASH(memPtmPatch.description);
|
||||
switch (entry->emc_freq_1) {
|
||||
case memPtmLimit:
|
||||
case memPtmAlt:
|
||||
case memPtmClamp:
|
||||
if (isMariko) {
|
||||
memPtmPatch.Apply(entry);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGGING("%u (0x%08x) @%p", entry->emc_freq_1, entry->conf_id, &(entry->emc_freq_2));
|
||||
CRASH("Unknown MEM Freq");
|
||||
}
|
||||
}
|
||||
|
||||
LOGGING("%s Count: %zu", cpuPtmBoostPatch.description, cpuPtmBoostPatch.patched_count);
|
||||
if (R_FAILED(cpuPtmBoostPatch.CheckResult()))
|
||||
CRASH(cpuPtmBoostPatch.description);
|
||||
|
||||
if (isMariko) {
|
||||
LOGGING("%s Count: %zu", memPtmPatch.description, memPtmPatch.patched_count);
|
||||
if (R_FAILED(memPtmPatch.CheckResult()))
|
||||
CRASH(memPtmPatch.description);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,26 +22,26 @@
|
||||
|
||||
namespace ams::ldr::hoc::ptm {
|
||||
|
||||
typedef struct {
|
||||
u32 conf_id;
|
||||
u32 cpu_freq_1; // min-max pair?
|
||||
u32 cpu_freq_2;
|
||||
u32 gpu_freq_1;
|
||||
u32 gpu_freq_2;
|
||||
u32 emc_freq_1;
|
||||
u32 emc_freq_2;
|
||||
u32 padding;
|
||||
} perf_conf_entry;
|
||||
typedef struct {
|
||||
u32 conf_id;
|
||||
u32 cpu_freq_1; // min-max pair?
|
||||
u32 cpu_freq_2;
|
||||
u32 gpu_freq_1;
|
||||
u32 gpu_freq_2;
|
||||
u32 emc_freq_1;
|
||||
u32 emc_freq_2;
|
||||
u32 padding;
|
||||
} perf_conf_entry;
|
||||
|
||||
constexpr u32 entryCnt = 16;
|
||||
constexpr u32 cpuPtmDefault = 1020'000'000;
|
||||
constexpr u32 cpuPtmDevOC = 1224'000'000;
|
||||
constexpr u32 cpuPtmBoost = 1785'000'000;
|
||||
constexpr u32 entryCnt = 16;
|
||||
constexpr u32 cpuPtmDefault = 1020'000'000;
|
||||
constexpr u32 cpuPtmDevOC = 1224'000'000;
|
||||
constexpr u32 cpuPtmBoost = 1785'000'000;
|
||||
|
||||
constexpr u32 memPtmLimit = 1600'000'000;
|
||||
constexpr u32 memPtmAlt = 1331'200'000;
|
||||
constexpr u32 memPtmClamp = 1065'600'000;
|
||||
constexpr u32 memPtmLimit = 1600'000'000;
|
||||
constexpr u32 memPtmAlt = 1331'200'000;
|
||||
constexpr u32 memPtmClamp = 1065'600'000;
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
-CSn
|
||||
/home/sould/Documents/GitHub/Horizon-OC/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.elf
|
||||
@@ -38,11 +38,11 @@ include $(DEVKITPRO)/libnx/switch_rules
|
||||
# NACP building is skipped as well.
|
||||
#---------------------------------------------------------------------------------
|
||||
APP_TITLE := Horizon OC Monitor
|
||||
APP_VERSION := 1.3.2+r4-hoc-r3
|
||||
APP_VERSION := 1.3.2+r4-hoc
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
INCLUDES := include lib/Atmosphere-libs/libstratosphere/source/dmnt lib/Atmosphere-libs/libstratosphere/source ../hoc-clk/common/include/
|
||||
INCLUDES := include lib/Atmosphere-libs/libstratosphere/source/dmnt lib/Atmosphere-libs/libstratosphere/source ../sys-clk/common/include/
|
||||
NO_ICON := 1
|
||||
#ROMFS := romfs
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Thanks to NaGa for Status Monitor Pro!
|
||||
Submodule Source/Horizon-OC-Monitor/lib/Atmosphere-libs updated: 132de9d7f2...6cc765fcaa
Submodule Source/Horizon-OC-Monitor/lib/libultrahand updated: 57fc30ac37...8541ecbe95
File diff suppressed because it is too large
Load Diff
@@ -129,8 +129,10 @@ public:
|
||||
//}
|
||||
//tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("Horizon OC Monitor", "Modes");
|
||||
if (!lastSelectedItem.empty())
|
||||
if (!lastSelectedItem.empty()) {
|
||||
list->jumpToItem(lastSelectedItem);
|
||||
}
|
||||
lastSelectedItem = "Other";
|
||||
|
||||
rootFrame->setContent(list);
|
||||
|
||||
@@ -156,7 +158,7 @@ public:
|
||||
}
|
||||
|
||||
if (keysDown & KEY_B) {
|
||||
lastSelectedItem = "Other";
|
||||
|
||||
tsl::swapTo<MainMenu>();
|
||||
triggerRumbleDoubleClick.store(true, std::memory_order_release);
|
||||
triggerExitSound.store(true, std::memory_order_release);
|
||||
@@ -367,8 +369,11 @@ public:
|
||||
});
|
||||
list->addItem(Other);
|
||||
|
||||
if (!lastSelectedItem.empty())
|
||||
if (!lastSelectedItem.empty()) {
|
||||
list->jumpToItem(lastSelectedItem);
|
||||
lastSelectedItem = "";
|
||||
}
|
||||
|
||||
|
||||
//list->disableCaching();
|
||||
tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("Horizon OC Monitor", APP_VERSION);
|
||||
@@ -441,14 +446,6 @@ public:
|
||||
if (SaltySD) {
|
||||
LoadSharedMemoryAndRefreshRate();
|
||||
}
|
||||
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
|
||||
uint32_t hocClkApiVer = 0;
|
||||
hocclkIpcGetAPIVersion(&hocClkApiVer);
|
||||
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
else hocclkCheck = 0;
|
||||
}
|
||||
if (R_SUCCEEDED(splInitialize())) {
|
||||
u64 sku = 0;
|
||||
splGetConfig(SplConfigItem_HardwareType, &sku);
|
||||
@@ -461,16 +458,14 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
shmemClose(&_sharedmemory);
|
||||
//Exit services
|
||||
clkrstExit();
|
||||
@@ -525,14 +520,6 @@ public:
|
||||
if (SaltySD) {
|
||||
LoadSharedMemory();
|
||||
}
|
||||
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
|
||||
uint32_t hocClkApiVer = 0;
|
||||
hocclkIpcGetAPIVersion(&hocClkApiVer);
|
||||
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
else hocclkCheck = 0;
|
||||
}
|
||||
if (R_SUCCEEDED(splInitialize())) {
|
||||
u64 sku = 0;
|
||||
splGetConfig(SplConfigItem_HardwareType, &sku);
|
||||
@@ -545,6 +532,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -552,9 +540,7 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
//Exit services
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
@@ -612,14 +598,6 @@ public:
|
||||
if (SaltySD) {
|
||||
LoadSharedMemory();
|
||||
}
|
||||
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
|
||||
uint32_t hocClkApiVer = 0;
|
||||
hocclkIpcGetAPIVersion(&hocClkApiVer);
|
||||
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
else hocclkCheck = 0;
|
||||
}
|
||||
if (R_SUCCEEDED(splInitialize())) {
|
||||
u64 sku = 0;
|
||||
splGetConfig(SplConfigItem_HardwareType, &sku);
|
||||
@@ -632,6 +610,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
|
||||
@@ -640,9 +619,7 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
// Exit services
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
@@ -702,14 +679,6 @@ public:
|
||||
if (SaltySD) {
|
||||
LoadSharedMemoryAndRefreshRate();
|
||||
}
|
||||
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
|
||||
uint32_t hocClkApiVer = 0;
|
||||
hocclkIpcGetAPIVersion(&hocClkApiVer);
|
||||
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
else hocclkCheck = 0;
|
||||
}
|
||||
if (R_SUCCEEDED(splInitialize())) {
|
||||
u64 sku = 0;
|
||||
splGetConfig(SplConfigItem_HardwareType, &sku);
|
||||
@@ -722,6 +691,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -729,9 +699,7 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
@@ -785,14 +753,6 @@ public:
|
||||
if (SaltySD) {
|
||||
LoadSharedMemoryAndRefreshRate();
|
||||
}
|
||||
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
|
||||
uint32_t hocClkApiVer = 0;
|
||||
hocclkIpcGetAPIVersion(&hocClkApiVer);
|
||||
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
else hocclkCheck = 0;
|
||||
}
|
||||
if (R_SUCCEEDED(splInitialize())) {
|
||||
u64 sku = 0;
|
||||
splGetConfig(SplConfigItem_HardwareType, &sku);
|
||||
@@ -805,6 +765,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -812,9 +773,7 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
@@ -868,14 +827,6 @@ public:
|
||||
if (SaltySD) {
|
||||
LoadSharedMemoryAndRefreshRate();
|
||||
}
|
||||
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
|
||||
uint32_t hocClkApiVer = 0;
|
||||
hocclkIpcGetAPIVersion(&hocClkApiVer);
|
||||
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
else hocclkCheck = 0;
|
||||
}
|
||||
if (R_SUCCEEDED(splInitialize())) {
|
||||
u64 sku = 0;
|
||||
splGetConfig(SplConfigItem_HardwareType, &sku);
|
||||
@@ -888,6 +839,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -895,9 +847,7 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
@@ -981,7 +931,7 @@ inline void setupMode(const std::string& modeType = "") {
|
||||
// This function gets called on startup to create a new Overlay object
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
// load heap settings outside of loop (only Status Monitor directive)
|
||||
// load heap settings outside of loop (only Horizon OC Monitor directive)
|
||||
ult::currentHeapSize = ult::getCurrentHeapSize();
|
||||
ult::expandedMemory = ult::currentHeapSize >= ult::OverlayHeapSize::Size_8MB;
|
||||
ult::limitedMemory = ult::currentHeapSize == ult::OverlayHeapSize::Size_4MB;
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
disableJumpTo = true;
|
||||
mutexInit(&mutex_BatteryChecker);
|
||||
StartBatteryThread();
|
||||
// tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
//tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
}
|
||||
~BatteryOverlay() {
|
||||
CloseBatteryThread();
|
||||
@@ -141,8 +141,8 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
// tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("Status Monitor Pro", APP_VERSION, true);
|
||||
//tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("Horizon OC Monitor", APP_VERSION, true);
|
||||
rootFrame->setContent(Status);
|
||||
|
||||
return rootFrame;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,9 +13,6 @@ private:
|
||||
char SOC_TEMP_c[12] = " -";
|
||||
char PCB_TEMP_c[12] = " -";
|
||||
char SKIN_TEMP_c[12] = " -";
|
||||
char CPU_TEMP_c[12] = " -";
|
||||
char GPU_TEMP_c[12] = " -";
|
||||
char RAM_TEMP_c[12] = " -";
|
||||
bool skipOnce = true;
|
||||
bool runOnce = true;
|
||||
|
||||
@@ -405,37 +402,19 @@ public:
|
||||
renderer->drawString("RAM", false, info_x, startY + lineHeight * 2+2*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(RAM_Load_c, false, value_x, startY + lineHeight * 2+2*SPACING, fontSize, settings.textColor);
|
||||
|
||||
// Line 3: CPU or SOC (with gradient color)
|
||||
if (settings.realTemps && realCPU_Temp != 0) {
|
||||
const tsl::Color cpuTempColor = settings.useDynamicColors ? tsl::GradientColor(realCPU_Temp / 1000.0f) : settings.textColor;
|
||||
renderer->drawString("CPU", false, info_x, startY + lineHeight * 3+3*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(CPU_TEMP_c, false, value_x, startY + lineHeight * 3+3*SPACING, fontSize, cpuTempColor);
|
||||
} else {
|
||||
renderer->drawString("SOC", false, info_x, startY + lineHeight * 3+3*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(SOC_TEMP_c, false, value_x, startY + lineHeight * 3+3*SPACING, fontSize, socColor);
|
||||
}
|
||||
|
||||
// Line 4: GPU or PCB (with gradient color)
|
||||
if (settings.realTemps && realGPU_Temp != 0) {
|
||||
const tsl::Color gpuTempColor = settings.useDynamicColors ? tsl::GradientColor(realGPU_Temp / 1000.0f) : settings.textColor;
|
||||
renderer->drawString("GPU", false, info_x, startY + lineHeight * 4+4*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(GPU_TEMP_c, false, value_x, startY + lineHeight * 4+4*SPACING, fontSize, gpuTempColor);
|
||||
} else {
|
||||
renderer->drawString("PCB", false, info_x, startY + lineHeight * 4+4*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(PCB_TEMP_c, false, value_x, startY + lineHeight * 4+4*SPACING, fontSize, pcbColor);
|
||||
}
|
||||
|
||||
// Line 5: RAM or SKIN (with gradient color)
|
||||
if (settings.realTemps && realRAM_Temp != 0) {
|
||||
const tsl::Color ramTempColor = settings.useDynamicColors ? tsl::GradientColor(realRAM_Temp / 1000.0f) : settings.textColor;
|
||||
renderer->drawString("RAM", false, info_x, startY + lineHeight * 5+5*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(RAM_TEMP_c, false, value_x, startY + lineHeight * 5+5*SPACING, fontSize, ramTempColor);
|
||||
} else {
|
||||
renderer->drawString("Skin", false, info_x, startY + lineHeight * 5+5*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(SKIN_TEMP_c, false, value_x, startY + lineHeight * 5+5*SPACING, fontSize, skinColor);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Line 3: SOC (with gradient color)
|
||||
renderer->drawString("SOC", false, info_x, startY + lineHeight * 3+3*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(SOC_TEMP_c, false, value_x, startY + lineHeight * 3+3*SPACING, fontSize, socColor);
|
||||
|
||||
// Line 4: PCB (with gradient color)
|
||||
renderer->drawString("PCB", false, info_x, startY + lineHeight * 4+4*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(PCB_TEMP_c, false, value_x, startY + lineHeight * 4+4*SPACING, fontSize, pcbColor);
|
||||
|
||||
// Line 5: SKIN (with gradient color)
|
||||
renderer->drawString("Skin", false, info_x, startY + lineHeight * 5+5*SPACING, fontSize, settings.catColor);
|
||||
renderer->drawString(SKIN_TEMP_c, false, value_x, startY + lineHeight * 5+5*SPACING, fontSize, skinColor);
|
||||
}
|
||||
});
|
||||
|
||||
tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("", "");
|
||||
rootFrame->setContent(Status);
|
||||
@@ -491,16 +470,6 @@ public:
|
||||
snprintf(PCB_TEMP_c, sizeof PCB_TEMP_c, "%2.1f\u00B0C", PCB_temperatureF);
|
||||
snprintf(SKIN_TEMP_c, sizeof SKIN_TEMP_c, "%2d.%d\u00B0C",
|
||||
skin_temperaturemiliC / 1000, (skin_temperaturemiliC / 100) % 10);
|
||||
|
||||
if (realCPU_Temp != 0) {
|
||||
snprintf(CPU_TEMP_c, sizeof(CPU_TEMP_c), "%.1f\u00B0C", realCPU_Temp / 1000.0f);
|
||||
}
|
||||
if (realGPU_Temp != 0) {
|
||||
snprintf(GPU_TEMP_c, sizeof(GPU_TEMP_c), "%.1f\u00B0C", realGPU_Temp / 1000.0f);
|
||||
}
|
||||
if (realRAM_Temp != 0) {
|
||||
snprintf(RAM_TEMP_c, sizeof(RAM_TEMP_c), "%.1f\u00B0C", realRAM_Temp / 1000.0f);
|
||||
}
|
||||
|
||||
// Atomically snapshot each idle tick once
|
||||
const uint64_t idle0 = idletick0.load(std::memory_order_acquire);
|
||||
@@ -527,8 +496,8 @@ public:
|
||||
snprintf(CPU_Load_c, sizeof(CPU_Load_c), "%.1f%%", cpu_usageM);
|
||||
snprintf(GPU_Load_c, sizeof(GPU_Load_c), "%d.%d%%", GPU_Load_u / 10, GPU_Load_u % 10);
|
||||
snprintf(RAM_Load_c, sizeof(RAM_Load_c), "%hu.%hhu%%",
|
||||
partLoad[HocClkPartLoad_EMC] / 10,
|
||||
partLoad[HocClkPartLoad_EMC] % 10);
|
||||
partLoad[SysClkPartLoad_EMC] / 10,
|
||||
partLoad[SysClkPartLoad_EMC] % 10);
|
||||
|
||||
mutexUnlock(&mutex_Misc);
|
||||
|
||||
|
||||
@@ -20,9 +20,6 @@ private:
|
||||
char SOC_temperature_c[32] = "";
|
||||
char PCB_temperature_c[32] = "";
|
||||
char skin_temperature_c[32] = "";
|
||||
char CPU_temp_c[32] = "";
|
||||
char GPU_temp_c[32] = "";
|
||||
char RAM_temp_c[32] = "";
|
||||
char BatteryDraw_c[64] = "";
|
||||
char FPS_var_compressed_c[64] = "";
|
||||
char RAM_load_c[64] = "";
|
||||
@@ -236,12 +233,10 @@ public:
|
||||
else if (realRAM_Hz && settings.showDeltas && (settings.showRealFreqs || settings.showTargetFreqs)) {
|
||||
renderer->drawString(DeltaRAM_c, false, COMMON_MARGIN + deltaOffset, height_offset, 15, (settings.textColor));
|
||||
}
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
static std::vector<std::string> partLoadColoredChars = {"CPU", "GPU"};
|
||||
//static auto loadLabelWidth = renderer->getTextDimensions("Load: ", false, 15).first;
|
||||
renderer->drawString("Load", false, COMMON_MARGIN, height_offset+15, 15, (settings.catColor2));
|
||||
renderer->drawStringWithColoredSections(RAM_load_c, false, partLoadColoredChars, COMMON_MARGIN + valueOffset, height_offset+15, 15, (settings.textColor), settings.catColor2);
|
||||
}
|
||||
static std::vector<std::string> PartLoadColoredChars = {"CPU", "GPU"};
|
||||
//static auto loadLabelWidth = renderer->getTextDimensions("Load: ", false, 15).first;
|
||||
renderer->drawString("Load", false, COMMON_MARGIN, height_offset+15, 15, (settings.catColor2));
|
||||
renderer->drawStringWithColoredSections(RAM_load_c, false, PartLoadColoredChars, COMMON_MARGIN + valueOffset, height_offset+15, 15, (settings.textColor), settings.catColor2);
|
||||
}
|
||||
if (R_SUCCEEDED(Hinted)) {
|
||||
//static auto textWidth = renderer->getTextDimensions("Total \nApplication \nApplet \nSystem \nSystem Unsafe ", false, 15).first;
|
||||
@@ -294,41 +289,6 @@ public:
|
||||
renderer->drawString(PCB_temperature_c, false, current_x, 620+2, 15, pcbColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Real temps - CPU, GPU, RAM
|
||||
if (settings.realTemps && (realCPU_Temp != 0 || realGPU_Temp != 0 || realRAM_Temp != 0)) {
|
||||
static auto cpuTempLabelWidth = renderer->getTextDimensions("CPU ", false, 15).first;
|
||||
static auto gpuTempLabelWidth = renderer->getTextDimensions("GPU ", false, 15).first;
|
||||
static auto ramTempLabelWidth = renderer->getTextDimensions("RAM ", false, 15).first;
|
||||
|
||||
uint32_t current_x = COMMON_MARGIN + 58;;
|
||||
|
||||
// CPU temp
|
||||
if (realCPU_Temp != 0) {
|
||||
const tsl::Color cpuTempColor = settings.useDynamicColors ? tsl::GradientColor(realCPU_Temp / 1000.0f) : settings.textColor;
|
||||
renderer->drawString("CPU ", false, current_x, 635+2, 15, (settings.catColor2));
|
||||
current_x += cpuTempLabelWidth;
|
||||
renderer->drawString(CPU_temp_c, false, current_x, 635+2, 15, cpuTempColor);
|
||||
current_x += renderer->getTextDimensions(CPU_temp_c, false, 15).first + 15;
|
||||
}
|
||||
|
||||
// GPU temp
|
||||
if (realGPU_Temp != 0) {
|
||||
const tsl::Color gpuTempColor = settings.useDynamicColors ? tsl::GradientColor(realGPU_Temp / 1000.0f) : settings.textColor;
|
||||
renderer->drawString("GPU ", false, current_x, 635+2, 15, (settings.catColor2));
|
||||
current_x += gpuTempLabelWidth;
|
||||
renderer->drawString(GPU_temp_c, false, current_x, 635+2, 15, gpuTempColor);
|
||||
current_x += renderer->getTextDimensions(GPU_temp_c, false, 15).first + 15;
|
||||
}
|
||||
|
||||
// RAM temp
|
||||
if (realRAM_Temp != 0) {
|
||||
const tsl::Color ramTempColor = settings.useDynamicColors ? tsl::GradientColor(realRAM_Temp / 1000.0f) : settings.textColor;
|
||||
renderer->drawString("RAM ", false, current_x, 635+2, 15, (settings.catColor2));
|
||||
current_x += ramTempLabelWidth;
|
||||
renderer->drawString(RAM_temp_c, false, current_x, 635+2, 15, ramTempColor);
|
||||
}
|
||||
}
|
||||
|
||||
///FPS
|
||||
if (GameRunning) {
|
||||
@@ -483,12 +443,12 @@ public:
|
||||
RAMPct_systemunsafe
|
||||
);
|
||||
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
const int RAM_GPU_Load = partLoad[HocClkPartLoad_EMC] - partLoad[HocClkPartLoad_EMCCpu];
|
||||
if (R_SUCCEEDED(sysclkCheck)) {
|
||||
const int RAM_GPU_Load = partLoad[SysClkPartLoad_EMC] - partLoad[SysClkPartLoad_EMCCpu];
|
||||
snprintf(RAM_load_c, sizeof RAM_load_c,
|
||||
"%u.%u%% CPU %u.%u%% GPU %u.%u%%",
|
||||
partLoad[HocClkPartLoad_EMC] / 10, partLoad[HocClkPartLoad_EMC] % 10,
|
||||
partLoad[HocClkPartLoad_EMCCpu] / 10, partLoad[HocClkPartLoad_EMCCpu] % 10,
|
||||
partLoad[SysClkPartLoad_EMC] / 10, partLoad[SysClkPartLoad_EMC] % 10,
|
||||
partLoad[SysClkPartLoad_EMCCpu] / 10, partLoad[SysClkPartLoad_EMCCpu] % 10,
|
||||
RAM_GPU_Load / 10, RAM_GPU_Load % 10);
|
||||
}
|
||||
///Thermal
|
||||
@@ -498,17 +458,6 @@ public:
|
||||
|
||||
snprintf(Rotation_SpeedLevel_c, sizeof Rotation_SpeedLevel_c, "%.1f%%", Rotation_Duty);
|
||||
|
||||
if (settings.realTemps) {
|
||||
if (realCPU_Temp != 0) {
|
||||
snprintf(CPU_temp_c, sizeof(CPU_temp_c), "%.1f°C", realCPU_Temp / 1000.0f);
|
||||
}
|
||||
if (realGPU_Temp != 0) {
|
||||
snprintf(GPU_temp_c, sizeof(GPU_temp_c), "%.1f°C", realGPU_Temp / 1000.0f);
|
||||
}
|
||||
if (realRAM_Temp != 0) {
|
||||
snprintf(RAM_temp_c, sizeof(RAM_temp_c), "%.1f°C", realRAM_Temp / 1000.0f);
|
||||
}
|
||||
}
|
||||
///FPS
|
||||
if (settings.showFPS == true) {
|
||||
snprintf(PFPS_value_c, sizeof PFPS_value_c, "%1u", FPS);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -50,7 +50,7 @@ public:
|
||||
|
||||
smExit();
|
||||
StartMiscThread();
|
||||
// tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
//tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
~MiscOverlay() {
|
||||
@@ -121,8 +121,8 @@ public:
|
||||
|
||||
});
|
||||
|
||||
// tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("Status Monitor Pro", APP_VERSION, true);
|
||||
//tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("Horizon OC Monitor", APP_VERSION, true);
|
||||
rootFrame->setContent(Status);
|
||||
|
||||
return rootFrame;
|
||||
|
||||
@@ -29,128 +29,125 @@
|
||||
#include <switch.h>
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#include <hocclk/client/ipc.h>
|
||||
#include <sysclk/client/ipc.h>
|
||||
|
||||
static Service g_hocclkSrv;
|
||||
static Service g_sysclkSrv;
|
||||
static atomic_size_t g_refCnt;
|
||||
|
||||
bool hocclkIpcRunning()
|
||||
bool sysclkIpcRunning()
|
||||
{
|
||||
Handle handle;
|
||||
bool running = R_FAILED(smRegisterService(&handle, smEncodeName(HOCCLK_IPC_SERVICE_NAME), false, 1));
|
||||
bool running = R_FAILED(smRegisterService(&handle, smEncodeName(SYSCLK_IPC_SERVICE_NAME), false, 1));
|
||||
|
||||
if (!running)
|
||||
{
|
||||
smUnregisterService(smEncodeName(HOCCLK_IPC_SERVICE_NAME));
|
||||
smUnregisterService(smEncodeName(SYSCLK_IPC_SERVICE_NAME));
|
||||
}
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
Result hocclkIpcInitialize(void)
|
||||
Result sysclkIpcInitialize(void)
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
g_refCnt++;
|
||||
|
||||
if (serviceIsActive(&g_hocclkSrv))
|
||||
if (serviceIsActive(&g_sysclkSrv))
|
||||
return 0;
|
||||
|
||||
rc = smGetService(&g_hocclkSrv, HOCCLK_IPC_SERVICE_NAME);
|
||||
rc = smGetService(&g_sysclkSrv, SYSCLK_IPC_SERVICE_NAME);
|
||||
|
||||
if (R_FAILED(rc)) hocclkIpcExit();
|
||||
if (R_FAILED(rc)) sysclkIpcExit();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void hocclkIpcExit(void)
|
||||
void sysclkIpcExit(void)
|
||||
{
|
||||
if (--g_refCnt == 0)
|
||||
{
|
||||
serviceClose(&g_hocclkSrv);
|
||||
serviceClose(&g_sysclkSrv);
|
||||
}
|
||||
}
|
||||
|
||||
Result hocclkIpcGetAPIVersion(u32* out_ver)
|
||||
Result sysclkIpcGetAPIVersion(u32* out_ver)
|
||||
{
|
||||
return serviceDispatchOut(&g_hocclkSrv, HocClkIpcCmd_GetApiVersion, *out_ver);
|
||||
return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetApiVersion, *out_ver);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetVersionString(char* out, size_t len)
|
||||
Result sysclkIpcGetVersionString(char* out, size_t len)
|
||||
{
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetVersionString,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = {{out, len}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetCurrentContext(HocClkContext* out_context)
|
||||
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
|
||||
{
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetCurrentContext,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_context, sizeof(HocClkContext)}},
|
||||
);
|
||||
return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext, *out_context);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetProfileCount(u64 tid, u8* out_count)
|
||||
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
|
||||
{
|
||||
return serviceDispatchInOut(&g_hocclkSrv, HocClkIpcCmd_GetProfileCount, tid, *out_count);
|
||||
return serviceDispatchInOut(&g_sysclkSrv, SysClkIpcCmd_GetProfileCount, tid, *out_count);
|
||||
}
|
||||
|
||||
Result hocclkIpcSetEnabled(bool enabled)
|
||||
Result sysclkIpcSetEnabled(bool enabled)
|
||||
{
|
||||
u8 enabledRaw = (u8)enabled;
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetEnabled, enabledRaw);
|
||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetEnabled, enabledRaw);
|
||||
}
|
||||
|
||||
Result hocclkIpcSetOverride(HocClkModule module, u32 hz)
|
||||
Result sysclkIpcSetOverride(SysClkModule module, u32 hz)
|
||||
{
|
||||
HocClkIpc_SetOverride_Args args = {
|
||||
SysClkIpc_SetOverride_Args args = {
|
||||
.module = module,
|
||||
.hz = hz
|
||||
};
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetOverride, args);
|
||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetOverride, args);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetProfiles(u64 tid, HocClkTitleProfileList* out_profiles)
|
||||
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
|
||||
{
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetProfiles, tid,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_profiles, sizeof(HocClkTitleProfileList)}},
|
||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcSetProfiles(u64 tid, HocClkTitleProfileList* profiles)
|
||||
Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles)
|
||||
{
|
||||
HocClkIpc_SetProfiles_Args args;
|
||||
SysClkIpc_SetProfiles_Args args;
|
||||
args.tid = tid;
|
||||
memcpy(&args.profiles, profiles, sizeof(HocClkTitleProfileList));
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetProfiles, args);
|
||||
memcpy(&args.profiles, profiles, sizeof(SysClkTitleProfileList));
|
||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetProfiles, args);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetConfigValues(HocClkConfigValueList* out_configValues)
|
||||
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
|
||||
{
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_configValues, sizeof(HocClkConfigValueList)}},
|
||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = {{out_configValues, sizeof(SysClkConfigValueList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcSetConfigValues(HocClkConfigValueList* configValues)
|
||||
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
|
||||
{
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_SetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
|
||||
.buffers = {{configValues, sizeof(HocClkConfigValueList)}},
|
||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = {{configValues, sizeof(SysClkConfigValueList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetFreqList(HocClkModule module, u32* list, u32 maxCount, u32* outCount)
|
||||
Result sysclkIpcGetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* outCount)
|
||||
{
|
||||
HocClkIpc_GetFreqList_Args args = {
|
||||
SysClkIpc_GetFreqList_Args args = {
|
||||
.module = module,
|
||||
.maxCount = maxCount
|
||||
};
|
||||
return serviceDispatchInOut(&g_hocclkSrv, HocClkIpcCmd_GetFreqList, args, *outCount,
|
||||
return serviceDispatchInOut(&g_sysclkSrv, SysClkIpcCmd_GetFreqList, args, *outCount,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{list, maxCount * sizeof(u32)}},
|
||||
);
|
||||
@@ -159,11 +156,11 @@ Result hocclkIpcGetFreqList(HocClkModule module, u32* list, u32 maxCount, u32* o
|
||||
Result hocClkIpcSetKipData()
|
||||
{
|
||||
u32 temp = 0;
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetKipData, temp);
|
||||
return serviceDispatchIn(&g_sysclkSrv, HocClkIpcCmd_SetKipData, temp);
|
||||
}
|
||||
|
||||
Result hocClkIpcGetKipData()
|
||||
{
|
||||
u32 temp = 0;
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetKipData, temp);
|
||||
return serviceDispatchIn(&g_sysclkSrv, HocClkIpcCmd_GetKipData, temp);
|
||||
}
|
||||
@@ -1,897 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "ldr_capabilities.hpp"
|
||||
#include "ldr_content_management.hpp"
|
||||
#include "ldr_development_manager.hpp"
|
||||
#include "ldr_launch_record.hpp"
|
||||
#include "ldr_meta.hpp"
|
||||
#include "ldr_patcher.hpp"
|
||||
#include "ldr_process_creation.hpp"
|
||||
#include "ldr_ro_manager.hpp"
|
||||
#include "oc/oc_loader.hpp"
|
||||
|
||||
namespace ams::ldr {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience defines. */
|
||||
constexpr size_t SystemResourceSizeMax = 0x1FE00000;
|
||||
constexpr size_t AutoLoadModuleSizeMax = 0x800000000;
|
||||
|
||||
/* Types. */
|
||||
enum NsoIndex {
|
||||
Nso_Rtld = 0,
|
||||
Nso_Main = 1,
|
||||
Nso_Wkc0 = 2,
|
||||
Nso_Wkc1 = 3,
|
||||
Nso_Wkc2 = 4,
|
||||
Nso_Wkc3 = 5,
|
||||
Nso_Wkc4 = 6,
|
||||
Nso_Wkc5 = 7,
|
||||
Nso_Wkc6 = 8,
|
||||
Nso_Wkc7 = 9,
|
||||
Nso_Wkc8 = 10,
|
||||
Nso_Wkc9 = 11,
|
||||
Nso_SubSdk0 = 12,
|
||||
Nso_SubSdk1 = 13,
|
||||
Nso_SubSdk2 = 14,
|
||||
Nso_SubSdk3 = 15,
|
||||
Nso_SubSdk4 = 16,
|
||||
Nso_SubSdk5 = 17,
|
||||
Nso_SubSdk6 = 18,
|
||||
Nso_SubSdk7 = 19,
|
||||
Nso_SubSdk8 = 20,
|
||||
Nso_SubSdk9 = 21,
|
||||
Nso_Sdk = 22,
|
||||
Nso_Count,
|
||||
};
|
||||
|
||||
constexpr inline const char *NsoPaths[Nso_Count] = {
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/rtld"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/main"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc0"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc1"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc2"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc3"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc4"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc5"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc6"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc7"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc8"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc9"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk0"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk1"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk2"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk3"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk4"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk5"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk6"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk7"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk8"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk9"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/sdk"),
|
||||
};
|
||||
|
||||
constexpr const char *GetNsoPath(size_t idx) {
|
||||
AMS_ABORT_UNLESS(idx < Nso_Count);
|
||||
return NsoPaths[idx];
|
||||
}
|
||||
|
||||
struct ProcessInfo {
|
||||
os::NativeHandle process_handle;
|
||||
uintptr_t args_address;
|
||||
size_t args_size;
|
||||
uintptr_t nso_address[Nso_Count];
|
||||
size_t nso_size[Nso_Count];
|
||||
};
|
||||
|
||||
struct AutoLoadModuleInfo {
|
||||
bool has_rtld;
|
||||
bool has_main;
|
||||
bool has_sdk;
|
||||
bool has_subsdk;
|
||||
bool has_nso[Nso_Count];
|
||||
};
|
||||
|
||||
/* Global NSO header cache. */
|
||||
NsoHeader g_nso_headers[Nso_Count];
|
||||
|
||||
/* Pcv/Ptm check cache */
|
||||
bool g_is_pcv;
|
||||
bool g_is_ptm;
|
||||
|
||||
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
|
||||
/* No version verification is done before 8.1.0. */
|
||||
R_SUCCEED_IF(hos::GetVersion() < hos::Version_8_1_0);
|
||||
|
||||
/* No verification is done if development. */
|
||||
R_SUCCEED_IF(IsDevelopmentForAntiDowngradeCheck());
|
||||
|
||||
/* TODO: Anti-downgrade checking does not make very much sense for us. Should we do anything? */
|
||||
AMS_UNUSED(program_id, version);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Helpers. */
|
||||
Result GetProgramInfoFromMeta(ProgramInfo *out, const Meta *meta) {
|
||||
/* Copy basic info. */
|
||||
out->main_thread_priority = meta->npdm->main_thread_priority;
|
||||
out->default_cpu_id = meta->npdm->default_cpu_id;
|
||||
out->main_thread_stack_size = meta->npdm->main_thread_stack_size;
|
||||
out->program_id = meta->aci->program_id;
|
||||
|
||||
/* Copy access controls. */
|
||||
size_t offset = 0;
|
||||
#define COPY_ACCESS_CONTROL(source, which) \
|
||||
({ \
|
||||
const size_t size = meta->source->which##_size; \
|
||||
R_UNLESS(offset + size <= sizeof(out->ac_buffer), ldr::ResultInternalError()); \
|
||||
out->source##_##which##_size = size; \
|
||||
std::memcpy(out->ac_buffer + offset, meta->source##_##which, size); \
|
||||
offset += size; \
|
||||
})
|
||||
|
||||
/* Copy all access controls to buffer. */
|
||||
COPY_ACCESS_CONTROL(acid, sac);
|
||||
COPY_ACCESS_CONTROL(aci, sac);
|
||||
COPY_ACCESS_CONTROL(acid, fac);
|
||||
COPY_ACCESS_CONTROL(aci, fah);
|
||||
#undef COPY_ACCESS_CONTROL
|
||||
|
||||
/* Copy flags. */
|
||||
out->flags = MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool IsApplet(const Meta *meta) {
|
||||
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet;
|
||||
}
|
||||
|
||||
bool IsApplication(const Meta *meta) {
|
||||
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Application;
|
||||
}
|
||||
|
||||
Npdm::AddressSpaceType GetAddressSpaceType(const Meta *meta) {
|
||||
return static_cast<Npdm::AddressSpaceType>((meta->npdm->flags & Npdm::MetaFlag_AddressSpaceTypeMask) >> Npdm::MetaFlag_AddressSpaceTypeShift);
|
||||
}
|
||||
|
||||
Acid::PoolPartition GetPoolPartition(const Meta *meta) {
|
||||
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
|
||||
}
|
||||
|
||||
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, AutoLoadModuleInfo *ali, u32 acid_flags) {
|
||||
/* Clear NSOs. */
|
||||
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
|
||||
*ali = {};
|
||||
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
/* Only load browser DLLs if acid flags say to do so. */
|
||||
switch (i) {
|
||||
case Nso_Wkc0:
|
||||
case Nso_Wkc1:
|
||||
case Nso_Wkc2:
|
||||
case Nso_Wkc3:
|
||||
case Nso_Wkc4:
|
||||
case Nso_Wkc5:
|
||||
case Nso_Wkc6:
|
||||
case Nso_Wkc7:
|
||||
case Nso_Wkc8:
|
||||
case Nso_Wkc9:
|
||||
if ((acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fs::FileHandle file;
|
||||
if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read))) {
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Read NSO header. */
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, nso_headers + i, sizeof(*nso_headers)));
|
||||
R_UNLESS(read_size == sizeof(*nso_headers), ldr::ResultInvalidNso());
|
||||
|
||||
/* Note nso is present. */
|
||||
switch (i) {
|
||||
case Nso_Rtld:
|
||||
ali->has_rtld = true;
|
||||
break;
|
||||
case Nso_Main:
|
||||
ali->has_main = true;
|
||||
break;
|
||||
case Nso_SubSdk0:
|
||||
case Nso_SubSdk1:
|
||||
case Nso_SubSdk2:
|
||||
case Nso_SubSdk3:
|
||||
case Nso_SubSdk4:
|
||||
case Nso_SubSdk5:
|
||||
case Nso_SubSdk6:
|
||||
case Nso_SubSdk7:
|
||||
case Nso_SubSdk8:
|
||||
case Nso_SubSdk9:
|
||||
ali->has_subsdk = true;
|
||||
break;
|
||||
case Nso_Sdk:
|
||||
ali->has_sdk = true;
|
||||
break;
|
||||
}
|
||||
ali->has_nso[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckAutoLoad(const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, u32 acid_flags) {
|
||||
/* We must always have a main. */
|
||||
R_UNLESS(ali->has_main, ldr::ResultInvalidNso());
|
||||
|
||||
/* All NSOs must not be --X. */
|
||||
/* This is "probably" not checked on Ounce? */
|
||||
for (size_t i = 0; i < Nso_Count; ++i) {
|
||||
R_UNLESS((nso_headers[i].flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* If we don't have an RTLD, we must only have a main. */
|
||||
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
|
||||
if (!ali->has_rtld) {
|
||||
/* If don't have rtld, we must also not have sdk. */
|
||||
R_UNLESS(!ali->has_sdk, ldr::ResultInvalidNso());
|
||||
|
||||
/* We must also not have both subsdk and browser dll. */
|
||||
R_UNLESS(!(ali->has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
|
||||
} else {
|
||||
/* If we have rtld, we must not have browser core dll. */
|
||||
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* Check NSO extents. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
/* Only validate the nsos we have. */
|
||||
if (!ali->has_nso[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* NSOs must have page-aligned segments. */
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].text_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].ro_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].rw_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
|
||||
/* NSOs must have zero text offset. */
|
||||
R_UNLESS(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso());
|
||||
|
||||
/* NSO .text must precede .rodata. */
|
||||
const size_t text_end = static_cast<size_t>(nso_headers[i].text_dst_offset) + static_cast<size_t>(nso_headers[i].text_size);
|
||||
R_UNLESS(text_end <= static_cast<size_t>(nso_headers[i].ro_dst_offset), ldr::ResultInvalidNso());
|
||||
|
||||
/* NSO .rodata must precede .rwdata. */
|
||||
const size_t ro_end = static_cast<size_t>(nso_headers[i].ro_dst_offset) + static_cast<size_t>(nso_headers[i].ro_size);
|
||||
R_UNLESS(ro_end <= static_cast<size_t>(nso_headers[i].rw_dst_offset), ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
constexpr const ncm::ProgramId UnqualifiedApprovalProgramIds[] = {
|
||||
{ 0x010003F003A34000 }, /* Pokemon: Let's Go, Pikachu! */
|
||||
{ 0x0100152000022000 }, /* Mario Kart 8 Deluxe */
|
||||
{ 0x0100165003504000 }, /* Nintendo Labo Toy-Con 04: VR Kit */
|
||||
{ 0x0100187003A36000 }, /* Pokemon: Let's Go, Eevee! */
|
||||
{ 0x01002E5008C56000 }, /* Pokemon Sword [Live Tournament] */
|
||||
{ 0x01002FF008C24000 }, /* Ring Fit Adventure */
|
||||
{ 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */
|
||||
{ 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */
|
||||
{ 0x01006F8002326000 }, /* Animal Crossing: New Horizons */
|
||||
{ 0x01006FB00F50E000 }, /* [???] */
|
||||
{ 0x010070300F50C000 }, /* [???] */
|
||||
{ 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */
|
||||
{ 0x01008DB008C2C000 }, /* Pokemon Shield */
|
||||
{ 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */
|
||||
{ 0x0100A66003384000 }, /* Hulu */
|
||||
{ 0x0100ABF008968000 }, /* Pokemon Sword */
|
||||
{ 0x0100C9A00ECE6000 }, /* Nintendo Switch Online (Nintendo 64) [for America] */
|
||||
{ 0x0100ED100BA3A000 }, /* Mario Kart Live: Home Circuit */
|
||||
{ 0x0100F38011CFE000 }, /* Animal Crossing: New Horizons Island Transfer Tool */
|
||||
{ 0x0100F6B011028000 }, /* 健身环大冒险 [Ring Fit Adventure for China] */
|
||||
};
|
||||
|
||||
/* Check that the unqualified approval programs are sorted. */
|
||||
static_assert([]() -> bool {
|
||||
for (size_t i = 0; i < util::size(UnqualifiedApprovalProgramIds) - 1; ++i) {
|
||||
if (UnqualifiedApprovalProgramIds[i].value >= UnqualifiedApprovalProgramIds[i + 1].value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}());
|
||||
|
||||
bool IsUnqualifiedApprovalProgramId(ncm::ProgramId program_id) {
|
||||
/* Check if the program id is one with unqualified approval. */
|
||||
return std::binary_search(std::begin(UnqualifiedApprovalProgramIds), std::end(UnqualifiedApprovalProgramIds), program_id);
|
||||
}
|
||||
|
||||
bool IsUnqualifiedApproval(const Meta *meta) {
|
||||
/* If the meta has unqualified approval flag, it's unqualified approval. */
|
||||
if (meta->acid->flags & ldr::Acid::AcidFlag_UnqualifiedApproval) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the unqualified approval flag is not set, the program must be an application. */
|
||||
if (!IsApplication(meta)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The program id must be a force unqualified approval program id. */
|
||||
return IsUnqualifiedApprovalProgramId(meta->acid->program_id_min) && meta->acid->program_id_min == meta->acid->program_id_max;
|
||||
}
|
||||
|
||||
Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeVerificationData &code_verification_data) {
|
||||
/* Validate version. */
|
||||
R_TRY(ValidateProgramVersion(loc.program_id, meta->npdm->version));
|
||||
|
||||
/* Validate program id. */
|
||||
R_UNLESS(meta->aci->program_id >= meta->acid->program_id_min, ldr::ResultInvalidProgramId());
|
||||
R_UNLESS(meta->aci->program_id <= meta->acid->program_id_max, ldr::ResultInvalidProgramId());
|
||||
|
||||
/* Validate the kernel capabilities. */
|
||||
R_TRY(TestCapability(static_cast<const util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)));
|
||||
|
||||
/* Check if NCA is PCV or PTM */
|
||||
g_is_pcv = meta->aci->program_id == ncm::SystemProgramId::Pcv;
|
||||
g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
|
||||
/* If we have data to validate, validate it. */
|
||||
if (meta->check_verification_data) {
|
||||
const u8 *sig = code_verification_data.signature;
|
||||
const size_t sig_size = sizeof(code_verification_data.signature);
|
||||
const u8 *mod = static_cast<u8 *>(meta->modulus);
|
||||
const size_t mod_size = crypto::Rsa2048PssSha256Verifier::ModulusSize;
|
||||
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
|
||||
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
|
||||
const u8 *hsh = code_verification_data.target_hash;
|
||||
const size_t hsh_size = sizeof(code_verification_data.target_hash);
|
||||
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size);
|
||||
|
||||
/* If the signature check fails, we need to check if this is allowable. */
|
||||
if (!is_signature_valid) {
|
||||
/* We have to enforce signature checks on prod and when we have a signature to check on dev. */
|
||||
R_UNLESS(IsDevelopmentForAcidProductionCheck(), ldr::ResultInvalidNcaSignature());
|
||||
R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature());
|
||||
|
||||
/* There was no signature to check on dev. Check if this is acceptable. */
|
||||
R_UNLESS(IsUnqualifiedApproval(meta), ldr::ResultInvalidNcaSignature());
|
||||
}
|
||||
}
|
||||
|
||||
/* All good. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
|
||||
const u8 meta_flags = meta->npdm->flags;
|
||||
|
||||
u32 flags = 0;
|
||||
|
||||
/* Set Is64Bit. */
|
||||
if (meta_flags & Npdm::MetaFlag_Is64Bit) {
|
||||
flags |= svc::CreateProcessFlag_Is64Bit;
|
||||
}
|
||||
|
||||
/* Set AddressSpaceType. */
|
||||
switch (GetAddressSpaceType(meta)) {
|
||||
case Npdm::AddressSpaceType_32Bit:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace32Bit;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_64BitDeprecated:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace64BitDeprecated;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_32BitWithoutAlias:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace32BitWithoutAlias;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_64Bit:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace64Bit;
|
||||
break;
|
||||
default:
|
||||
R_THROW(ldr::ResultInvalidMeta());
|
||||
}
|
||||
|
||||
/* Set Enable Debug. */
|
||||
if (ldr_flags & CreateProcessFlag_EnableDebug) {
|
||||
flags |= svc::CreateProcessFlag_EnableDebug;
|
||||
}
|
||||
|
||||
/* Set Enable ASLR. */
|
||||
if (!(ldr_flags & CreateProcessFlag_DisableAslr)) {
|
||||
flags |= svc::CreateProcessFlag_EnableAslr;
|
||||
}
|
||||
|
||||
/* Set Is Application. */
|
||||
if (IsApplication(meta)) {
|
||||
flags |= svc::CreateProcessFlag_IsApplication;
|
||||
|
||||
/* 7.0.0+: Set OptimizeMemoryAllocation if relevant. */
|
||||
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
||||
if (meta_flags & Npdm::MetaFlag_OptimizeMemoryAllocation) {
|
||||
flags |= svc::CreateProcessFlag_OptimizeMemoryAllocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 5.0.0+ Set Pool Partition. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
/* TODO: Nintendo no longer accepts Applet when pool partition == application. Would this break hbl/anything else in the hb ecosystem? */
|
||||
/* TODO: Nintendo uses a helper bool MakeSvcPoolPartitionFlag(u32 *out, Acid::PoolPartition partition); */
|
||||
switch (GetPoolPartition(meta)) {
|
||||
case Acid::PoolPartition_Application:
|
||||
if (IsApplet(meta)) {
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||
} else {
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplication;
|
||||
}
|
||||
break;
|
||||
case Acid::PoolPartition_Applet:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||
break;
|
||||
case Acid::PoolPartition_System:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionSystem;
|
||||
break;
|
||||
case Acid::PoolPartition_SystemNonSecure:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure;
|
||||
break;
|
||||
default:
|
||||
R_THROW(ldr::ResultInvalidMeta());
|
||||
}
|
||||
} else if (hos::GetVersion() >= hos::Version_4_0_0) {
|
||||
/* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */
|
||||
if (meta->acid->flags & Acid::AcidFlag_DeprecatedUseSecureMemory) {
|
||||
flags |= svc::CreateProcessFlag_DeprecatedUseSecureMemory;
|
||||
}
|
||||
}
|
||||
|
||||
/* 11.0.0+/meso Set Disable DAS merge. */
|
||||
if (meta_flags & Npdm::MetaFlag_DisableDeviceAddressSpaceMerge) {
|
||||
flags |= svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge;
|
||||
}
|
||||
|
||||
/* 18.0.0+/meso Set Alias region extra size. */
|
||||
if (meta_flags & Npdm::MetaFlag_EnableAliasRegionExtraSize) {
|
||||
flags |= svc::CreateProcessFlag_EnableAliasRegionExtraSize;
|
||||
}
|
||||
|
||||
*out = flags;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetCreateProcessParameter(svc::CreateProcessParameter *out, const Meta *meta, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Clear output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Set name, version, program id, resource limit handle. */
|
||||
std::memcpy(out->name, meta->npdm->program_name, sizeof(out->name) - 1);
|
||||
out->version = meta->npdm->version;
|
||||
out->program_id = meta->aci->program_id.value;
|
||||
out->reslimit = resource_limit;
|
||||
|
||||
/* Set flags. */
|
||||
R_TRY(GetCreateProcessFlags(std::addressof(out->flags), meta, flags));
|
||||
|
||||
/* 3.0.0+ System Resource Size. */
|
||||
if (hos::GetVersion() >= hos::Version_3_0_0) {
|
||||
/* Validate size is aligned. */
|
||||
R_UNLESS(util::IsAligned(meta->npdm->system_resource_size, os::MemoryBlockUnitSize), ldr::ResultInvalidSize());
|
||||
|
||||
/* Validate system resource usage. */
|
||||
if (meta->npdm->system_resource_size) {
|
||||
/* Process must be 64-bit. */
|
||||
R_UNLESS((out->flags & svc::CreateProcessFlag_AddressSpace64Bit), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Process must be application or applet. */
|
||||
R_UNLESS(IsApplication(meta) || IsApplet(meta), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Size must be less than or equal to max. */
|
||||
R_UNLESS(meta->npdm->system_resource_size <= SystemResourceSizeMax, ldr::ResultInvalidMeta());
|
||||
}
|
||||
out->system_resource_num_pages = meta->npdm->system_resource_size >> 12;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u64 GenerateSecureRandom(u64 max) {
|
||||
/* Generate a cryptographically random number. */
|
||||
u64 rand;
|
||||
crypto::GenerateCryptographicallyRandomBytes(std::addressof(rand), sizeof(rand));
|
||||
|
||||
/* Coerce into range. */
|
||||
return rand % (max + 1);
|
||||
}
|
||||
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
|
||||
/* Clear output. */
|
||||
out->args_address = 0;
|
||||
out->args_size = 0;
|
||||
std::memset(out->nso_address, 0, sizeof(out->nso_address));
|
||||
std::memset(out->nso_size, 0, sizeof(out->nso_size));
|
||||
|
||||
size_t total_size = 0;
|
||||
bool argument_allocated = false;
|
||||
|
||||
/* Calculate base offsets. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (ali->has_nso[i]) {
|
||||
out->nso_address[i] = total_size;
|
||||
const size_t text_end = static_cast<size_t>(nso_headers[i].text_dst_offset) + static_cast<size_t>(nso_headers[i].text_size);
|
||||
const size_t ro_end = static_cast<size_t>(nso_headers[i].ro_dst_offset) + static_cast<size_t>(nso_headers[i].ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(nso_headers[i].rw_dst_offset) + static_cast<size_t>(nso_headers[i].rw_size);
|
||||
out->nso_size[i] = text_end;
|
||||
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
|
||||
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
|
||||
out->nso_size[i] += static_cast<size_t>(nso_headers[i].bss_size);
|
||||
|
||||
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());
|
||||
|
||||
out->nso_size[i] = aligned_up_size;
|
||||
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->nso_size[i]), ldr::ResultInvalidNso());
|
||||
total_size += out->nso_size[i];
|
||||
|
||||
if (!argument_allocated && argument != nullptr) {
|
||||
out->args_address = total_size;
|
||||
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
|
||||
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->args_size), ldr::ResultInvalidNso());
|
||||
total_size += out->args_size;
|
||||
|
||||
argument_allocated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate ASLR. */
|
||||
uintptr_t aslr_start = 0;
|
||||
size_t aslr_size = 0;
|
||||
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
||||
switch (out_param->flags & svc::CreateProcessFlag_AddressSpaceMask) {
|
||||
case svc::CreateProcessFlag_AddressSpace32Bit:
|
||||
case svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
|
||||
aslr_start = svc::AddressSmallMap32Start;
|
||||
aslr_size = svc::AddressSmallMap32Size;
|
||||
break;
|
||||
case svc::CreateProcessFlag_AddressSpace64BitDeprecated:
|
||||
aslr_start = svc::AddressSmallMap36Start;
|
||||
aslr_size = svc::AddressSmallMap36Size;
|
||||
break;
|
||||
case svc::CreateProcessFlag_AddressSpace64Bit:
|
||||
aslr_start = svc::AddressMap39Start;
|
||||
aslr_size = svc::AddressMap39Size;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
} else {
|
||||
/* On 1.0.0, only 2 address space types existed. */
|
||||
if (out_param->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) {
|
||||
aslr_start = svc::AddressSmallMap36Start;
|
||||
aslr_size = svc::AddressSmallMap36Size;
|
||||
} else {
|
||||
aslr_start = svc::AddressSmallMap32Start;
|
||||
aslr_size = svc::AddressSmallMap32Size;
|
||||
}
|
||||
}
|
||||
R_UNLESS(total_size <= aslr_size, svc::ResultOutOfMemory());
|
||||
|
||||
/* Set Create Process output. */
|
||||
uintptr_t aslr_slide = 0;
|
||||
size_t free_size = (aslr_size - total_size);
|
||||
if (out_param->flags & svc::CreateProcessFlag_EnableAslr) {
|
||||
aslr_slide = GenerateSecureRandom(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize;
|
||||
}
|
||||
|
||||
/* Set out. */
|
||||
aslr_start += aslr_slide;
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (ali->has_nso[i]) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->nso_address[i], aslr_start), ldr::ResultInvalidNso());
|
||||
out->nso_address[i] += aslr_start;
|
||||
}
|
||||
}
|
||||
if (out->args_address) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->args_address, aslr_start), ldr::ResultInvalidNso());
|
||||
out->args_address += aslr_start;
|
||||
}
|
||||
|
||||
out_param->code_address = aslr_start;
|
||||
out_param->code_num_pages = total_size >> 12;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) {
|
||||
/* Select read size based on compression. */
|
||||
if (!is_compressed) {
|
||||
file_size = segment->size;
|
||||
}
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(file_size <= segment->size, ldr::ResultInvalidNso());
|
||||
R_UNLESS(segment->size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
|
||||
/* Load data from file. */
|
||||
uintptr_t load_address = is_compressed ? map_end - file_size : map_base;
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, segment->file_offset, reinterpret_cast<void *>(load_address), file_size));
|
||||
R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
|
||||
|
||||
/* Uncompress if necessary. */
|
||||
if (is_compressed) {
|
||||
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment->size, reinterpret_cast<const void *>(load_address), file_size) == static_cast<int>(segment->size));
|
||||
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* Check hash if necessary. */
|
||||
if (check_hash) {
|
||||
u8 hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::GenerateSha256(hash, sizeof(hash), reinterpret_cast<void *>(map_base), segment->size);
|
||||
|
||||
R_UNLESS(std::memcmp(hash, file_hash, sizeof(hash)) == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) {
|
||||
/* Map and read data from file. */
|
||||
{
|
||||
/* Map the process memory. */
|
||||
void *mapped_memory = nullptr;
|
||||
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); };
|
||||
|
||||
const uintptr_t map_address = reinterpret_cast<uintptr_t>(mapped_memory);
|
||||
|
||||
/* Load NSO segments. */
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
|
||||
|
||||
/* Clear unused space to zero. */
|
||||
const size_t text_end = static_cast<size_t>(nso_header->text_dst_offset) + static_cast<size_t>(nso_header->text_size);
|
||||
const size_t ro_end = static_cast<size_t>(nso_header->ro_dst_offset) + static_cast<size_t>(nso_header->ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(nso_header->rw_dst_offset) + static_cast<size_t>(nso_header->rw_size);
|
||||
std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
|
||||
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size);
|
||||
|
||||
/* Apply embedded patches. */
|
||||
ApplyEmbeddedPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* Apply IPS patches. */
|
||||
LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* Apply PCV and PTM patches */
|
||||
if (g_is_pcv) {
|
||||
hoc::pcv::Patch(map_address, nso_size);
|
||||
}
|
||||
|
||||
if (g_is_ptm) {
|
||||
hoc::ptm::Patch(map_address, nso_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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));
|
||||
}
|
||||
if (ro_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||
}
|
||||
if (rw_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
|
||||
/* Load each NSO. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (ali->has_nso[i]) {
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Load arguments, if present. */
|
||||
if (argument != nullptr) {
|
||||
/* Write argument data into memory. */
|
||||
{
|
||||
void *map_address = nullptr;
|
||||
R_TRY(os::MapProcessMemory(std::addressof(map_address), process_info->process_handle, process_info->args_address, process_info->args_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); };
|
||||
|
||||
ProgramArguments *args = static_cast<ProgramArguments *>(map_address);
|
||||
std::memset(args, 0, sizeof(*args));
|
||||
args->allocated_size = process_info->args_size;
|
||||
args->arguments_size = argument->argument_size;
|
||||
std::memcpy(args->arguments, argument->argument, argument->argument_size);
|
||||
}
|
||||
|
||||
/* Set argument region permissions. */
|
||||
/* NOTE: Nintendo uses svc::SetProcessMemoryPermission directly here. */
|
||||
R_TRY(os::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, os::MemoryPermission_ReadWrite));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Get CreateProcessParameter. */
|
||||
svc::CreateProcessParameter param;
|
||||
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
||||
|
||||
/* Decide on an NSO layout. */
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, ali, argument));
|
||||
|
||||
/* Actually create process. */
|
||||
svc::Handle process_handle;
|
||||
R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast<const u32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(u32)));
|
||||
|
||||
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
|
||||
out->process_handle = process_handle;
|
||||
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
||||
|
||||
/* Load all auto load modules. */
|
||||
R_RETURN(LoadAutoLoadModules(out, nso_headers, ali, argument));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Process Creation API. */
|
||||
Result CreateProcess(os::NativeHandle *out, PinId pin_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status, const char *path, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit, const ldr::ProgramAttributes &attrs) {
|
||||
/* Mount code. */
|
||||
AMS_UNUSED(path);
|
||||
ScopedCodeMountForCode mount(loc, override_status, attrs);
|
||||
R_TRY(mount.GetResult());
|
||||
|
||||
/* Load meta, possibly from cache. */
|
||||
Meta meta;
|
||||
R_TRY(LoadMetaFromCache(std::addressof(meta), loc, override_status, attrs.platform));
|
||||
|
||||
/* Validate meta. */
|
||||
R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData()));
|
||||
|
||||
/* If we should, load/validate the browser core dll. */
|
||||
util::optional<ScopedCodeMountForBrowserCoreDll> bdll_mount;
|
||||
if ((meta.acid->flags & Acid::AcidFlag_LoadBrowserCoreDll)) {
|
||||
/* NOTE: I'm unsure whether we should be getting a fresh override status (allowing for different override between main and bdll?) */
|
||||
/* or whether we should be using the main override status. Going to go with main, for sanity's sake. */
|
||||
/* Also noting that Nintendo always passes ProgramAttributes=0 here, but this "should" be different on Ounce? */
|
||||
/* Kind of unclear how to handle this without knowing what exactly is being ifdef'd. */
|
||||
const ncm::ProgramLocation bdll_loc = ncm::ProgramLocation::Make(ncm::SystemProgramId::BrowserCoreDll, ncm::StorageId::BuiltInSystem);
|
||||
const cfg::OverrideStatus bdll_override_status = override_status;
|
||||
const ldr::ProgramAttributes bdll_attrs = attrs;
|
||||
bdll_mount.emplace(bdll_loc, bdll_override_status, bdll_attrs);
|
||||
R_TRY(bdll_mount->GetResult());
|
||||
|
||||
/* Load browser dll meta, possibly from cache. */
|
||||
Meta bdll_meta;
|
||||
R_TRY(LoadMetaFromCacheForBrowserCoreDll(std::addressof(bdll_meta), bdll_loc, bdll_override_status, bdll_attrs.platform));
|
||||
|
||||
/* Validate browser dll meta. */
|
||||
R_TRY(ValidateMeta(std::addressof(bdll_meta), loc, mount.GetCodeVerificationData()));
|
||||
}
|
||||
|
||||
/* Load, validate NSO headers. */
|
||||
AutoLoadModuleInfo auto_load_info = {};
|
||||
R_TRY(LoadAutoLoadHeaders(g_nso_headers, std::addressof(auto_load_info), meta.acid->flags));
|
||||
R_TRY(CheckAutoLoad(g_nso_headers, std::addressof(auto_load_info), meta.acid->flags));
|
||||
|
||||
/* Actually create the process and load NSOs into process memory. */
|
||||
ProcessInfo info;
|
||||
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, std::addressof(auto_load_info), argument, flags, resource_limit));
|
||||
|
||||
/* Register NSOs with the RoManager. */
|
||||
{
|
||||
/* Nintendo doesn't validate this get, but we do. */
|
||||
os::ProcessId process_id = os::GetProcessId(info.process_handle);
|
||||
|
||||
/* Register new process. */
|
||||
const auto as_type = GetAddressSpaceType(std::addressof(meta));
|
||||
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::AddressSpaceType_64Bit || as_type == Npdm::AddressSpaceType_64BitDeprecated);
|
||||
|
||||
/* Register all NSOs. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (auto_load_info.has_nso[i]) {
|
||||
RoManager::GetInstance().AddNso(pin_id, g_nso_headers[i].module_id, info.nso_address[i], info.nso_size[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're overriding for HBL, perform HTML document redirection. */
|
||||
if (override_status.IsHbl()) {
|
||||
/* Don't validate result, failure is okay. */
|
||||
RedirectHtmlDocumentPathForHbl(loc);
|
||||
}
|
||||
|
||||
/* Clear the external code for the program. */
|
||||
fssystem::DestroyExternalCode(loc.program_id);
|
||||
|
||||
/* Note that we've created the program. */
|
||||
SetLaunchedBootProgram(loc.program_id);
|
||||
|
||||
/* Move the process handle to output. */
|
||||
*out = info.process_handle;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProgramInfo(ProgramInfo *out, cfg::OverrideStatus *out_status, const ncm::ProgramLocation &loc, const char *path, const ldr::ProgramAttributes &attrs) {
|
||||
Meta meta;
|
||||
|
||||
/* Load Meta. */
|
||||
{
|
||||
AMS_UNUSED(path);
|
||||
|
||||
ScopedCodeMountForCode mount(loc, attrs);
|
||||
R_TRY(mount.GetResult());
|
||||
R_TRY(LoadMeta(std::addressof(meta), loc, mount.GetOverrideStatus(), attrs.platform, false));
|
||||
if (out_status != nullptr) {
|
||||
*out_status = mount.GetOverrideStatus();
|
||||
}
|
||||
}
|
||||
|
||||
return GetProgramInfoFromMeta(out, std::addressof(meta));
|
||||
}
|
||||
|
||||
Result PinProgram(PinId *out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status) {
|
||||
R_UNLESS(RoManager::GetInstance().Allocate(out_id, loc, override_status), ldr::ResultMaxProcess());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UnpinProgram(PinId id) {
|
||||
R_UNLESS(RoManager::GetInstance().Free(id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
|
||||
R_UNLESS(RoManager::GetInstance().GetProcessModuleInfo(out_count, out, max_out_count, process_id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProgramLocationAndOverrideStatusFromPinId(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id) {
|
||||
R_UNLESS(RoManager::GetInstance().GetProgramLocationAndStatus(out, out_status, pin_id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,938 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "ldr_capabilities.hpp"
|
||||
#include "ldr_content_management.hpp"
|
||||
#include "ldr_development_manager.hpp"
|
||||
#include "ldr_launch_record.hpp"
|
||||
#include "ldr_meta.hpp"
|
||||
#include "ldr_patcher.hpp"
|
||||
#include "ldr_process_creation.hpp"
|
||||
#include "ldr_ro_manager.hpp"
|
||||
#include "oc/oc_loader.hpp"
|
||||
|
||||
namespace ams::ldr {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience defines. */
|
||||
constexpr size_t SystemResourceSizeMax = 0x1FE00000;
|
||||
constexpr size_t AutoLoadModuleSizeMax = 0x800000000;
|
||||
|
||||
/* Types. */
|
||||
enum NsoIndex {
|
||||
Nso_Rtld = 0,
|
||||
Nso_Main = 1,
|
||||
Nso_Wkc0 = 2,
|
||||
Nso_Wkc1 = 3,
|
||||
Nso_Wkc2 = 4,
|
||||
Nso_Wkc3 = 5,
|
||||
Nso_Wkc4 = 6,
|
||||
Nso_Wkc5 = 7,
|
||||
Nso_Wkc6 = 8,
|
||||
Nso_Wkc7 = 9,
|
||||
Nso_Wkc8 = 10,
|
||||
Nso_Wkc9 = 11,
|
||||
Nso_SubSdk0 = 12,
|
||||
Nso_SubSdk1 = 13,
|
||||
Nso_SubSdk2 = 14,
|
||||
Nso_SubSdk3 = 15,
|
||||
Nso_SubSdk4 = 16,
|
||||
Nso_SubSdk5 = 17,
|
||||
Nso_SubSdk6 = 18,
|
||||
Nso_SubSdk7 = 19,
|
||||
Nso_SubSdk8 = 20,
|
||||
Nso_SubSdk9 = 21,
|
||||
Nso_Sdk = 22,
|
||||
Nso_Count,
|
||||
};
|
||||
|
||||
constexpr inline const char *NsoPaths[Nso_Count] = {
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/rtld"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/main"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc0"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc1"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc2"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc3"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc4"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc5"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc6"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc7"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc8"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc9"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk0"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk1"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk2"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk3"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk4"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk5"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk6"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk7"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk8"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk9"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/sdk"),
|
||||
};
|
||||
|
||||
constexpr const char *GetNsoPath(size_t idx) {
|
||||
AMS_ABORT_UNLESS(idx < Nso_Count);
|
||||
return NsoPaths[idx];
|
||||
}
|
||||
|
||||
struct ProcessInfo {
|
||||
os::NativeHandle process_handle;
|
||||
uintptr_t code_address;
|
||||
size_t total_size;
|
||||
uintptr_t args_address;
|
||||
size_t args_size;
|
||||
uintptr_t nso_address[Nso_Count];
|
||||
size_t nso_size[Nso_Count];
|
||||
};
|
||||
|
||||
struct AutoLoadModuleInfo {
|
||||
bool has_rtld;
|
||||
bool has_main;
|
||||
bool has_sdk;
|
||||
bool has_subsdk;
|
||||
s8 nso_indices[Nso_Count];
|
||||
};
|
||||
|
||||
struct AutoLoadModuleContext {
|
||||
NsoHeader *headers;
|
||||
int nso_count;
|
||||
int rtld_idx;
|
||||
int main_nso_idx;
|
||||
int sdk_nso_idx;
|
||||
AutoLoadModuleInfo ali;
|
||||
};
|
||||
|
||||
/* Global NSO header cache. */
|
||||
NsoHeader g_nso_headers[Nso_Count];
|
||||
|
||||
/* Pcv/Ptm check cache */
|
||||
bool g_is_pcv;
|
||||
bool g_is_ptm;
|
||||
|
||||
/* Global Zstd decompression context. */
|
||||
constexpr size_t ZstdDctxWorkspaceSize = 0x176E8;
|
||||
alignas(8) u8 g_zstd_dctx_workspace[ZstdDctxWorkspaceSize];
|
||||
|
||||
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
|
||||
/* No version verification is done before 8.1.0. */
|
||||
R_SUCCEED_IF(hos::GetVersion() < hos::Version_8_1_0);
|
||||
|
||||
/* No verification is done if development. */
|
||||
R_SUCCEED_IF(IsDevelopmentForAntiDowngradeCheck());
|
||||
|
||||
/* TODO: Anti-downgrade checking does not make very much sense for us. Should we do anything? */
|
||||
AMS_UNUSED(program_id, version);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Helpers. */
|
||||
Result GetProgramInfoFromMeta(ProgramInfo *out, const Meta *meta) {
|
||||
/* Copy basic info. */
|
||||
out->main_thread_priority = meta->npdm->main_thread_priority;
|
||||
out->default_cpu_id = meta->npdm->default_cpu_id;
|
||||
out->main_thread_stack_size = meta->npdm->main_thread_stack_size;
|
||||
out->program_id = meta->aci->program_id;
|
||||
|
||||
/* Copy access controls. */
|
||||
size_t offset = 0;
|
||||
#define COPY_ACCESS_CONTROL(source, which) \
|
||||
({ \
|
||||
const size_t size = meta->source->which##_size; \
|
||||
R_UNLESS(offset + size <= sizeof(out->ac_buffer), ldr::ResultInternalError()); \
|
||||
out->source##_##which##_size = size; \
|
||||
std::memcpy(out->ac_buffer + offset, meta->source##_##which, size); \
|
||||
offset += size; \
|
||||
})
|
||||
|
||||
/* Copy all access controls to buffer. */
|
||||
COPY_ACCESS_CONTROL(acid, sac);
|
||||
COPY_ACCESS_CONTROL(aci, sac);
|
||||
COPY_ACCESS_CONTROL(acid, fac);
|
||||
COPY_ACCESS_CONTROL(aci, fah);
|
||||
#undef COPY_ACCESS_CONTROL
|
||||
|
||||
/* Copy flags. */
|
||||
out->flags = MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool IsApplet(const Meta *meta) {
|
||||
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet;
|
||||
}
|
||||
|
||||
bool IsApplication(const Meta *meta) {
|
||||
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Application;
|
||||
}
|
||||
|
||||
Npdm::AddressSpaceType GetAddressSpaceType(const Meta *meta) {
|
||||
return static_cast<Npdm::AddressSpaceType>((meta->npdm->flags & Npdm::MetaFlag_AddressSpaceTypeMask) >> Npdm::MetaFlag_AddressSpaceTypeShift);
|
||||
}
|
||||
|
||||
Acid::PoolPartition GetPoolPartition(const Meta *meta) {
|
||||
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
|
||||
}
|
||||
|
||||
Result LoadAutoLoadHeaders(AutoLoadModuleContext &ctx, u32 acid_flags) {
|
||||
/* Clear NSOs. */
|
||||
std::memset(g_nso_headers, 0, sizeof(g_nso_headers));
|
||||
ctx.headers = g_nso_headers;
|
||||
ctx.nso_count = 0;
|
||||
ctx.rtld_idx = -1;
|
||||
ctx.main_nso_idx = -1;
|
||||
ctx.sdk_nso_idx = -1;
|
||||
ctx.ali = {};
|
||||
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
/* Only load browser DLLs if acid flags say to do so. */
|
||||
switch (i) {
|
||||
case Nso_Wkc0:
|
||||
case Nso_Wkc1:
|
||||
case Nso_Wkc2:
|
||||
case Nso_Wkc3:
|
||||
case Nso_Wkc4:
|
||||
case Nso_Wkc5:
|
||||
case Nso_Wkc6:
|
||||
case Nso_Wkc7:
|
||||
case Nso_Wkc8:
|
||||
case Nso_Wkc9:
|
||||
if ((acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fs::FileHandle file;
|
||||
if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read))) {
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Read NSO header. */
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, g_nso_headers + ctx.nso_count, sizeof(NsoHeader)));
|
||||
R_UNLESS(read_size == sizeof(NsoHeader), ldr::ResultInvalidNso());
|
||||
|
||||
/* Note nso is present. */
|
||||
switch (i) {
|
||||
case Nso_Rtld:
|
||||
ctx.rtld_idx = ctx.nso_count;
|
||||
ctx.ali.has_rtld = true;
|
||||
break;
|
||||
case Nso_Main:
|
||||
ctx.main_nso_idx = ctx.nso_count;
|
||||
ctx.ali.has_main = true;
|
||||
break;
|
||||
case Nso_SubSdk0:
|
||||
case Nso_SubSdk1:
|
||||
case Nso_SubSdk2:
|
||||
case Nso_SubSdk3:
|
||||
case Nso_SubSdk4:
|
||||
case Nso_SubSdk5:
|
||||
case Nso_SubSdk6:
|
||||
case Nso_SubSdk7:
|
||||
case Nso_SubSdk8:
|
||||
case Nso_SubSdk9:
|
||||
ctx.ali.has_subsdk = true;
|
||||
break;
|
||||
case Nso_Sdk:
|
||||
ctx.sdk_nso_idx = ctx.nso_count;
|
||||
ctx.ali.has_sdk = true;
|
||||
break;
|
||||
}
|
||||
ctx.ali.nso_indices[ctx.nso_count] = static_cast<s8>(i);
|
||||
ctx.nso_count++;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckAutoLoad(const AutoLoadModuleContext &ctx, u32 acid_flags) {
|
||||
/* We must always have a main. */
|
||||
R_UNLESS(ctx.ali.has_main, ldr::ResultInvalidNso());
|
||||
|
||||
/* Validate flags and extents for all present NSOs. */
|
||||
for (int i = 0; i < ctx.nso_count; ++i) {
|
||||
const auto &hdr = ctx.headers[i];
|
||||
|
||||
/* All NSOs must not be --X. */
|
||||
/* This is "probably" not checked on Ounce? */
|
||||
R_UNLESS((hdr.flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
|
||||
|
||||
/* Zstd compression only allowed on main, and only when both rtld+sdk are present. */
|
||||
if (i != ctx.main_nso_idx || ctx.rtld_idx < 0 || ctx.sdk_nso_idx < 0) {
|
||||
R_UNLESS((hdr.flags & NsoHeader::Flag_UseZbicCompression) == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* NSOs must have page-aligned segments. */
|
||||
R_UNLESS(util::IsAligned(hdr.text_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(hdr.ro_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(hdr.rw_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
|
||||
/* NSOs must have zero text offset. */
|
||||
R_UNLESS(hdr.text_dst_offset == 0, ldr::ResultInvalidNso());
|
||||
|
||||
/* NSO .text must precede .rodata. */
|
||||
const size_t text_end = static_cast<size_t>(hdr.text_dst_offset) + static_cast<size_t>(hdr.text_size);
|
||||
R_UNLESS(text_end <= static_cast<size_t>(hdr.ro_dst_offset), ldr::ResultInvalidNso());
|
||||
|
||||
/* NSO .rodata must precede .rwdata. */
|
||||
const size_t ro_end = static_cast<size_t>(hdr.ro_dst_offset) + static_cast<size_t>(hdr.ro_size);
|
||||
R_UNLESS(ro_end <= static_cast<size_t>(hdr.rw_dst_offset), ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
|
||||
if (ctx.ali.has_rtld || ctx.ali.has_sdk) {
|
||||
/* If we have sdk we must have rtld. */
|
||||
R_UNLESS(ctx.ali.has_rtld, ldr::ResultInvalidNso());
|
||||
|
||||
/* If we have rtld, we must not have browser core dll. */
|
||||
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
|
||||
} else {
|
||||
/* We must not have both subsdk and browser dll. */
|
||||
R_UNLESS(!(ctx.ali.has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
constexpr const ncm::ProgramId UnqualifiedApprovalProgramIds[] = {
|
||||
{ 0x010003F003A34000 }, /* Pokemon: Let's Go, Pikachu! */
|
||||
{ 0x0100152000022000 }, /* Mario Kart 8 Deluxe */
|
||||
{ 0x0100165003504000 }, /* Nintendo Labo Toy-Con 04: VR Kit */
|
||||
{ 0x0100187003A36000 }, /* Pokemon: Let's Go, Eevee! */
|
||||
{ 0x01002E5008C56000 }, /* Pokemon Sword [Live Tournament] */
|
||||
{ 0x01002FF008C24000 }, /* Ring Fit Adventure */
|
||||
{ 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */
|
||||
{ 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */
|
||||
{ 0x01006F8002326000 }, /* Animal Crossing: New Horizons */
|
||||
{ 0x01006FB00F50E000 }, /* 宝可梦 走吧!伊布 [Pokemon: Let's Go, Eevee! for China] */
|
||||
{ 0x010070300F50C000 }, /* 宝可梦 走吧!皮卡丘 [Pokemon: Let's Go, Pikachu! for China] */
|
||||
{ 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */
|
||||
{ 0x01008DB008C2C000 }, /* Pokemon Shield */
|
||||
{ 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */
|
||||
{ 0x0100A66003384000 }, /* Hulu */
|
||||
{ 0x0100ABF008968000 }, /* Pokemon Sword */
|
||||
{ 0x0100C9A00ECE6000 }, /* Nintendo Switch Online (Nintendo 64) [for America] */
|
||||
{ 0x0100ED100BA3A000 }, /* Mario Kart Live: Home Circuit */
|
||||
{ 0x0100F38011CFE000 }, /* Animal Crossing: New Horizons Island Transfer Tool */
|
||||
{ 0x0100F6B011028000 }, /* 健身环大冒险 [Ring Fit Adventure for China] */
|
||||
};
|
||||
|
||||
/* Check that the unqualified approval programs are sorted. */
|
||||
static_assert([]() -> bool {
|
||||
for (size_t i = 0; i < util::size(UnqualifiedApprovalProgramIds) - 1; ++i) {
|
||||
if (UnqualifiedApprovalProgramIds[i].value >= UnqualifiedApprovalProgramIds[i + 1].value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}());
|
||||
|
||||
bool IsUnqualifiedApprovalProgramId(ncm::ProgramId program_id) {
|
||||
/* Check if the program id is one with unqualified approval. */
|
||||
return std::binary_search(std::begin(UnqualifiedApprovalProgramIds), std::end(UnqualifiedApprovalProgramIds), program_id);
|
||||
}
|
||||
|
||||
bool IsUnqualifiedApproval(const Meta *meta) {
|
||||
/* If the meta has unqualified approval flag, it's unqualified approval. */
|
||||
if (meta->acid->flags & ldr::Acid::AcidFlag_UnqualifiedApproval) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the unqualified approval flag is not set, the program must be an application. */
|
||||
if (!IsApplication(meta)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The program id must be a force unqualified approval program id. */
|
||||
return IsUnqualifiedApprovalProgramId(meta->acid->program_id_min) && meta->acid->program_id_min == meta->acid->program_id_max;
|
||||
}
|
||||
|
||||
Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeVerificationData &code_verification_data) {
|
||||
/* Validate version. */
|
||||
R_TRY(ValidateProgramVersion(loc.program_id, meta->npdm->version));
|
||||
|
||||
/* Validate program id. */
|
||||
R_UNLESS(meta->aci->program_id >= meta->acid->program_id_min, ldr::ResultInvalidProgramId());
|
||||
R_UNLESS(meta->aci->program_id <= meta->acid->program_id_max, ldr::ResultInvalidProgramId());
|
||||
|
||||
/* Validate the kernel capabilities. */
|
||||
R_TRY(TestCapability(static_cast<const util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)));
|
||||
|
||||
/* Check if NCA is PCV or PTM */
|
||||
g_is_pcv = meta->aci->program_id == ncm::SystemProgramId::Pcv;
|
||||
g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
|
||||
/* If we have data to validate, validate it. */
|
||||
if (meta->check_verification_data) {
|
||||
const u8 *sig = code_verification_data.signature;
|
||||
const size_t sig_size = sizeof(code_verification_data.signature);
|
||||
const u8 *mod = static_cast<u8 *>(meta->modulus);
|
||||
const size_t mod_size = crypto::Rsa2048PssSha256Verifier::ModulusSize;
|
||||
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
|
||||
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
|
||||
const u8 *hsh = code_verification_data.target_hash;
|
||||
const size_t hsh_size = sizeof(code_verification_data.target_hash);
|
||||
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size);
|
||||
|
||||
/* If the signature check fails, we need to check if this is allowable. */
|
||||
if (!is_signature_valid) {
|
||||
/* We have to enforce signature checks on prod and when we have a signature to check on dev. */
|
||||
R_UNLESS(IsDevelopmentForAcidProductionCheck(), ldr::ResultInvalidNcaSignature());
|
||||
R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature());
|
||||
|
||||
/* There was no signature to check on dev. Check if this is acceptable. */
|
||||
R_UNLESS(IsUnqualifiedApproval(meta), ldr::ResultInvalidNcaSignature());
|
||||
}
|
||||
}
|
||||
|
||||
/* All good. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
|
||||
const u8 meta_flags = meta->npdm->flags;
|
||||
|
||||
u32 flags = 0;
|
||||
|
||||
/* Set Is64Bit. */
|
||||
if (meta_flags & Npdm::MetaFlag_Is64Bit) {
|
||||
flags |= svc::CreateProcessFlag_Is64Bit;
|
||||
}
|
||||
|
||||
/* Set AddressSpaceType. */
|
||||
switch (GetAddressSpaceType(meta)) {
|
||||
case Npdm::AddressSpaceType_32Bit:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace32Bit;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_64BitDeprecated:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace64BitDeprecated;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_32BitWithoutAlias:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace32BitWithoutAlias;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_64Bit:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace64Bit;
|
||||
break;
|
||||
default:
|
||||
R_THROW(ldr::ResultInvalidMeta());
|
||||
}
|
||||
|
||||
/* Set Enable Debug. */
|
||||
if (ldr_flags & CreateProcessFlag_EnableDebug) {
|
||||
flags |= svc::CreateProcessFlag_EnableDebug;
|
||||
}
|
||||
|
||||
/* Set Enable ASLR. */
|
||||
if (!(ldr_flags & CreateProcessFlag_DisableAslr)) {
|
||||
flags |= svc::CreateProcessFlag_EnableAslr;
|
||||
}
|
||||
|
||||
/* Set Is Application. */
|
||||
if (IsApplication(meta)) {
|
||||
flags |= svc::CreateProcessFlag_IsApplication;
|
||||
|
||||
/* 7.0.0+: Set OptimizeMemoryAllocation if relevant. */
|
||||
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
||||
if (meta_flags & Npdm::MetaFlag_OptimizeMemoryAllocation) {
|
||||
flags |= svc::CreateProcessFlag_OptimizeMemoryAllocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 5.0.0+ Set Pool Partition. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
/* TODO: Nintendo no longer accepts Applet when pool partition == application. Would this break hbl/anything else in the hb ecosystem? */
|
||||
/* TODO: Nintendo uses a helper bool MakeSvcPoolPartitionFlag(u32 *out, Acid::PoolPartition partition); */
|
||||
switch (GetPoolPartition(meta)) {
|
||||
case Acid::PoolPartition_Application:
|
||||
if (IsApplet(meta)) {
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||
} else {
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplication;
|
||||
}
|
||||
break;
|
||||
case Acid::PoolPartition_Applet:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||
break;
|
||||
case Acid::PoolPartition_System:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionSystem;
|
||||
break;
|
||||
case Acid::PoolPartition_SystemNonSecure:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure;
|
||||
break;
|
||||
default:
|
||||
R_THROW(ldr::ResultInvalidMeta());
|
||||
}
|
||||
} else if (hos::GetVersion() >= hos::Version_4_0_0) {
|
||||
/* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */
|
||||
if (meta->acid->flags & Acid::AcidFlag_DeprecatedUseSecureMemory) {
|
||||
flags |= svc::CreateProcessFlag_DeprecatedUseSecureMemory;
|
||||
}
|
||||
}
|
||||
|
||||
/* 11.0.0+/meso Set Disable DAS merge. */
|
||||
if (meta_flags & Npdm::MetaFlag_DisableDeviceAddressSpaceMerge) {
|
||||
flags |= svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge;
|
||||
}
|
||||
|
||||
/* 18.0.0+/meso Set Alias region extra size. */
|
||||
if (meta_flags & Npdm::MetaFlag_EnableAliasRegionExtraSize) {
|
||||
flags |= svc::CreateProcessFlag_EnableAliasRegionExtraSize;
|
||||
}
|
||||
|
||||
*out = flags;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetCreateProcessParameter(svc::CreateProcessParameter *out, const Meta *meta, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Clear output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Set name, version, program id, resource limit handle. */
|
||||
std::memcpy(out->name, meta->npdm->program_name, sizeof(out->name) - 1);
|
||||
out->version = meta->npdm->version;
|
||||
out->program_id = meta->aci->program_id.value;
|
||||
out->reslimit = resource_limit;
|
||||
|
||||
/* Set flags. */
|
||||
R_TRY(GetCreateProcessFlags(std::addressof(out->flags), meta, flags));
|
||||
|
||||
/* 3.0.0+ System Resource Size. */
|
||||
if (hos::GetVersion() >= hos::Version_3_0_0) {
|
||||
/* Validate size is aligned. */
|
||||
R_UNLESS(util::IsAligned(meta->npdm->system_resource_size, os::MemoryBlockUnitSize), ldr::ResultInvalidSize());
|
||||
|
||||
/* Validate system resource usage. */
|
||||
if (meta->npdm->system_resource_size) {
|
||||
/* Process must be 64-bit. */
|
||||
R_UNLESS((out->flags & svc::CreateProcessFlag_AddressSpace64Bit), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Process must be application or applet. */
|
||||
R_UNLESS(IsApplication(meta) || IsApplet(meta), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Size must be less than or equal to max. */
|
||||
R_UNLESS(meta->npdm->system_resource_size <= SystemResourceSizeMax, ldr::ResultInvalidMeta());
|
||||
}
|
||||
out->system_resource_num_pages = meta->npdm->system_resource_size >> 12;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u64 GenerateSecureRandom(u64 max) {
|
||||
/* Generate a cryptographically random number. */
|
||||
u64 rand;
|
||||
crypto::GenerateCryptographicallyRandomBytes(std::addressof(rand), sizeof(rand));
|
||||
|
||||
/* Coerce into range. */
|
||||
return rand % (max + 1);
|
||||
}
|
||||
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument) {
|
||||
/* Clear output. */
|
||||
out->args_address = 0;
|
||||
out->args_size = 0;
|
||||
std::memset(out->nso_address, 0, sizeof(out->nso_address));
|
||||
std::memset(out->nso_size, 0, sizeof(out->nso_size));
|
||||
|
||||
size_t total_size = 0;
|
||||
bool argument_allocated = false;
|
||||
|
||||
/* Calculate base offsets. */
|
||||
for (int i = 0; i < ctx.nso_count; i++) {
|
||||
out->nso_address[i] = total_size;
|
||||
const size_t text_end = static_cast<size_t>(ctx.headers[i].text_dst_offset) + static_cast<size_t>(ctx.headers[i].text_size);
|
||||
const size_t ro_end = static_cast<size_t>(ctx.headers[i].ro_dst_offset) + static_cast<size_t>(ctx.headers[i].ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(ctx.headers[i].rw_dst_offset) + static_cast<size_t>(ctx.headers[i].rw_size);
|
||||
out->nso_size[i] = text_end;
|
||||
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
|
||||
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
|
||||
out->nso_size[i] += static_cast<size_t>(ctx.headers[i].bss_size);
|
||||
|
||||
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());
|
||||
|
||||
out->nso_size[i] = aligned_up_size;
|
||||
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->nso_size[i]), ldr::ResultInvalidNso());
|
||||
total_size += out->nso_size[i];
|
||||
|
||||
if (!argument_allocated && argument != nullptr) {
|
||||
out->args_address = total_size;
|
||||
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
|
||||
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->args_size), ldr::ResultInvalidNso());
|
||||
total_size += out->args_size;
|
||||
|
||||
argument_allocated = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate ASLR. */
|
||||
uintptr_t aslr_start = 0;
|
||||
size_t aslr_size = 0;
|
||||
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
||||
switch (out_param->flags & svc::CreateProcessFlag_AddressSpaceMask) {
|
||||
case svc::CreateProcessFlag_AddressSpace32Bit:
|
||||
case svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
|
||||
aslr_start = svc::AddressSmallMap32Start;
|
||||
aslr_size = svc::AddressSmallMap32Size;
|
||||
break;
|
||||
case svc::CreateProcessFlag_AddressSpace64BitDeprecated:
|
||||
aslr_start = svc::AddressSmallMap36Start;
|
||||
aslr_size = svc::AddressSmallMap36Size;
|
||||
break;
|
||||
case svc::CreateProcessFlag_AddressSpace64Bit:
|
||||
aslr_start = svc::AddressMap39Start;
|
||||
aslr_size = svc::AddressMap39Size;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
} else {
|
||||
/* On 1.0.0, only 2 address space types existed. */
|
||||
if (out_param->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) {
|
||||
aslr_start = svc::AddressSmallMap36Start;
|
||||
aslr_size = svc::AddressSmallMap36Size;
|
||||
} else {
|
||||
aslr_start = svc::AddressSmallMap32Start;
|
||||
aslr_size = svc::AddressSmallMap32Size;
|
||||
}
|
||||
}
|
||||
R_UNLESS(total_size <= aslr_size, svc::ResultOutOfMemory());
|
||||
|
||||
/* Set Create Process output. */
|
||||
uintptr_t aslr_slide = 0;
|
||||
size_t free_size = (aslr_size - total_size);
|
||||
if (out_param->flags & svc::CreateProcessFlag_EnableAslr) {
|
||||
aslr_slide = GenerateSecureRandom(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize;
|
||||
}
|
||||
|
||||
/* Set out. */
|
||||
aslr_start += aslr_slide;
|
||||
for (int i = 0; i < ctx.nso_count; i++) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->nso_address[i], aslr_start), ldr::ResultInvalidNso());
|
||||
out->nso_address[i] += aslr_start;
|
||||
}
|
||||
if (out->args_address) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->args_address, aslr_start), ldr::ResultInvalidNso());
|
||||
out->args_address += aslr_start;
|
||||
}
|
||||
|
||||
out_param->code_address = aslr_start;
|
||||
out_param->code_num_pages = total_size >> 12;
|
||||
out->total_size = total_size;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModuleSegment(fs::FileHandle file, size_t file_offset, size_t compressed_size, size_t segment_size, bool is_compressed, bool is_zstd, uintptr_t map_base, uintptr_t map_end) {
|
||||
/* Select read size based on compression. */
|
||||
size_t file_size = is_compressed ? compressed_size : segment_size;
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(file_size <= segment_size, ldr::ResultInvalidNso());
|
||||
R_UNLESS(file_size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
R_UNLESS(segment_size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
|
||||
/* Load data from file. */
|
||||
uintptr_t load_address = is_compressed ? map_end - compressed_size : map_base;
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, file_offset, reinterpret_cast<void *>(load_address), file_size));
|
||||
R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
|
||||
|
||||
/* Uncompress if necessary. */
|
||||
R_SUCCEED_IF(!is_compressed);
|
||||
|
||||
auto compressed_data_buf = reinterpret_cast<const void *>(load_address);
|
||||
|
||||
if (is_zstd) {
|
||||
bool decompressed = util::DecompressZstdForLoader(reinterpret_cast<void *>(g_zstd_dctx_workspace), ZstdDctxWorkspaceSize, reinterpret_cast<void *>(map_base), static_cast<size_t>(map_end - map_base), segment_size, compressed_data_buf, file_size);
|
||||
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||
} else {
|
||||
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment_size, compressed_data_buf, file_size) == static_cast<int>(segment_size));
|
||||
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckSegmentHash(const NsoHeader *nso_header, uintptr_t map_address, NsoHeader::Segment segment) {
|
||||
if ((nso_header->flags & (NsoHeader::Flag_CheckHashText << segment)) == 0) {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u8 hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::GenerateSha256(hash, sizeof(hash),
|
||||
reinterpret_cast<void *>(map_address + nso_header->segments[segment].dst_offset),
|
||||
nso_header->segments[segment].size);
|
||||
R_UNLESS(std::memcmp(hash, nso_header->segment_hashes[segment], sizeof(hash)) == 0, ldr::ResultInvalidNso());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Map and read data from file. */
|
||||
{
|
||||
/* Map the process memory. */
|
||||
void *mapped_memory = nullptr;
|
||||
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, map_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, map_size); };
|
||||
|
||||
const uintptr_t map_address = reinterpret_cast<uintptr_t>(mapped_memory);
|
||||
const uintptr_t map_end = map_address + map_size;
|
||||
|
||||
/* Load NSO segments. */
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Text].file_offset, nso_header->text_compressed_size, nso_header->text_size,
|
||||
(nso_header->flags & NsoHeader::Flag_CompressedText) != 0, is_zstd, map_address + nso_header->text_dst_offset, map_end));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Ro].file_offset, nso_header->ro_compressed_size, nso_header->ro_size,
|
||||
(nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, is_zstd, map_address + nso_header->ro_dst_offset, map_end));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Rw].file_offset, nso_header->rw_compressed_size, nso_header->rw_size,
|
||||
(nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, is_zstd, map_address + nso_header->rw_dst_offset, map_end));
|
||||
|
||||
/* Clear unused space to zero. */
|
||||
const size_t text_end = static_cast<size_t>(nso_header->text_dst_offset) + static_cast<size_t>(nso_header->text_size);
|
||||
const size_t ro_end = static_cast<size_t>(nso_header->ro_dst_offset) + static_cast<size_t>(nso_header->ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(nso_header->rw_dst_offset) + static_cast<size_t>(nso_header->rw_size);
|
||||
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_size - rw_end);
|
||||
|
||||
/* Check segment hashes. */
|
||||
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Text));
|
||||
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Ro));
|
||||
R_TRY(CheckSegmentHash(nso_header, map_address, NsoHeader::Segment_Rw));
|
||||
|
||||
/* Apply embedded patches. */
|
||||
ApplyEmbeddedPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* Apply IPS patches. */
|
||||
LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* Apply PCV and PTM patches */
|
||||
if (g_is_pcv) {
|
||||
hoc::pcv::Patch(map_address, nso_size);
|
||||
}
|
||||
|
||||
if (g_is_ptm) {
|
||||
hoc::ptm::Patch(map_address, nso_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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));
|
||||
}
|
||||
if (ro_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||
}
|
||||
if (rw_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument) {
|
||||
/* Load each NSO. */
|
||||
const uintptr_t total_end = process_info->code_address + process_info->total_size;
|
||||
|
||||
for (int i = 0; i < ctx.nso_count; i++) {
|
||||
const NsoIndex nso_idx = static_cast<NsoIndex>(ctx.ali.nso_indices[i]);
|
||||
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(nso_idx), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/* Load arguments, if present. */
|
||||
if (argument != nullptr) {
|
||||
/* Write argument data into memory. */
|
||||
{
|
||||
void *map_address = nullptr;
|
||||
R_TRY(os::MapProcessMemory(std::addressof(map_address), process_info->process_handle, process_info->args_address, process_info->args_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); };
|
||||
|
||||
ProgramArguments *args = static_cast<ProgramArguments *>(map_address);
|
||||
std::memset(args, 0, sizeof(*args));
|
||||
args->allocated_size = process_info->args_size;
|
||||
args->arguments_size = argument->argument_size;
|
||||
std::memcpy(args->arguments, argument->argument, argument->argument_size);
|
||||
}
|
||||
|
||||
/* Set argument region permissions. */
|
||||
/* NOTE: Nintendo uses svc::SetProcessMemoryPermission directly here. */
|
||||
R_TRY(os::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, os::MemoryPermission_ReadWrite));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Get CreateProcessParameter. */
|
||||
svc::CreateProcessParameter param;
|
||||
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
||||
|
||||
/* Decide on an NSO layout. */
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), ctx, argument));
|
||||
|
||||
/* Actually create process. */
|
||||
svc::Handle process_handle;
|
||||
R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast<const u32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(u32)));
|
||||
|
||||
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
|
||||
out->process_handle = process_handle;
|
||||
out->code_address = param.code_address;
|
||||
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
||||
|
||||
/* Load all auto load modules. */
|
||||
R_RETURN(LoadAutoLoadModules(out, ctx, argument));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Process Creation API. */
|
||||
Result CreateProcess(os::NativeHandle *out, PinId pin_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status, const char *path, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit, const ldr::ProgramAttributes &attrs) {
|
||||
/* Mount code. */
|
||||
AMS_UNUSED(path);
|
||||
ScopedCodeMountForCode mount(loc, override_status, attrs);
|
||||
R_TRY(mount.GetResult());
|
||||
|
||||
/* Load meta, possibly from cache. */
|
||||
Meta meta;
|
||||
R_TRY(LoadMetaFromCache(std::addressof(meta), loc, override_status, attrs.platform));
|
||||
|
||||
/* Validate meta. */
|
||||
R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData()));
|
||||
|
||||
/* If we should, load/validate the browser core dll. */
|
||||
util::optional<ScopedCodeMountForBrowserCoreDll> bdll_mount;
|
||||
if ((meta.acid->flags & Acid::AcidFlag_LoadBrowserCoreDll)) {
|
||||
/* NOTE: I'm unsure whether we should be getting a fresh override status (allowing for different override between main and bdll?) */
|
||||
/* or whether we should be using the main override status. Going to go with main, for sanity's sake. */
|
||||
/* Also noting that Nintendo always passes ProgramAttributes=0 here, but this "should" be different on Ounce? */
|
||||
/* Kind of unclear how to handle this without knowing what exactly is being ifdef'd. */
|
||||
const ncm::ProgramLocation bdll_loc = ncm::ProgramLocation::Make(ncm::SystemProgramId::BrowserCoreDll, ncm::StorageId::BuiltInSystem);
|
||||
const cfg::OverrideStatus bdll_override_status = override_status;
|
||||
const ldr::ProgramAttributes bdll_attrs = attrs;
|
||||
bdll_mount.emplace(bdll_loc, bdll_override_status, bdll_attrs);
|
||||
R_TRY(bdll_mount->GetResult());
|
||||
|
||||
/* Load browser dll meta, possibly from cache. */
|
||||
Meta bdll_meta;
|
||||
R_TRY(LoadMetaFromCacheForBrowserCoreDll(std::addressof(bdll_meta), bdll_loc, bdll_override_status, bdll_attrs.platform));
|
||||
|
||||
/* Validate browser dll meta. */
|
||||
R_TRY(ValidateMeta(std::addressof(bdll_meta), loc, mount.GetCodeVerificationData()));
|
||||
}
|
||||
|
||||
/* Load, validate NSO headers. */
|
||||
AutoLoadModuleContext ctx;
|
||||
R_TRY(LoadAutoLoadHeaders(ctx, meta.acid->flags));
|
||||
R_TRY(CheckAutoLoad(ctx, meta.acid->flags));
|
||||
|
||||
/* Actually create the process and load NSOs into process memory. */
|
||||
ProcessInfo info;
|
||||
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), ctx, argument, flags, resource_limit));
|
||||
|
||||
/* Register NSOs with the RoManager. */
|
||||
{
|
||||
/* Nintendo doesn't validate this get, but we do. */
|
||||
os::ProcessId process_id = os::GetProcessId(info.process_handle);
|
||||
|
||||
/* Register new process. */
|
||||
const auto as_type = GetAddressSpaceType(std::addressof(meta));
|
||||
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::AddressSpaceType_64Bit || as_type == Npdm::AddressSpaceType_64BitDeprecated);
|
||||
|
||||
/* Register all NSOs. */
|
||||
for (int i = 0; i < ctx.nso_count; i++) {
|
||||
RoManager::GetInstance().AddNso(pin_id, ctx.headers[i].module_id, info.nso_address[i], info.nso_size[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're overriding for HBL, perform HTML document redirection. */
|
||||
if (override_status.IsHbl()) {
|
||||
/* Don't validate result, failure is okay. */
|
||||
RedirectHtmlDocumentPathForHbl(loc);
|
||||
}
|
||||
|
||||
/* Clear the external code for the program. */
|
||||
fssystem::DestroyExternalCode(loc.program_id);
|
||||
|
||||
/* Note that we've created the program. */
|
||||
SetLaunchedBootProgram(loc.program_id);
|
||||
|
||||
/* Move the process handle to output. */
|
||||
*out = info.process_handle;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProgramInfo(ProgramInfo *out, cfg::OverrideStatus *out_status, const ncm::ProgramLocation &loc, const char *path, const ldr::ProgramAttributes &attrs) {
|
||||
Meta meta;
|
||||
|
||||
/* Load Meta. */
|
||||
{
|
||||
AMS_UNUSED(path);
|
||||
|
||||
ScopedCodeMountForCode mount(loc, attrs);
|
||||
R_TRY(mount.GetResult());
|
||||
R_TRY(LoadMeta(std::addressof(meta), loc, mount.GetOverrideStatus(), attrs.platform, false));
|
||||
if (out_status != nullptr) {
|
||||
*out_status = mount.GetOverrideStatus();
|
||||
}
|
||||
}
|
||||
|
||||
return GetProgramInfoFromMeta(out, std::addressof(meta));
|
||||
}
|
||||
|
||||
Result PinProgram(PinId *out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status) {
|
||||
R_UNLESS(RoManager::GetInstance().Allocate(out_id, loc, override_status), ldr::ResultMaxProcess());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UnpinProgram(PinId id) {
|
||||
R_UNLESS(RoManager::GetInstance().Free(id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
|
||||
R_UNLESS(RoManager::GetInstance().GetProcessModuleInfo(out_count, out, max_out_count, process_id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProgramLocationAndOverrideStatusFromPinId(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id) {
|
||||
R_UNLESS(RoManager::GetInstance().GetProgramLocationAndStatus(out, out_status, pin_id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,780 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "ldr_capabilities.hpp"
|
||||
#include "ldr_content_management.hpp"
|
||||
#include "ldr_development_manager.hpp"
|
||||
#include "ldr_launch_record.hpp"
|
||||
#include "ldr_meta.hpp"
|
||||
#include "ldr_patcher.hpp"
|
||||
#include "ldr_process_creation.hpp"
|
||||
#include "ldr_ro_manager.hpp"
|
||||
#include "ldr_ro_manager.hpp"
|
||||
#include "oc/oc_loader.hpp"
|
||||
|
||||
namespace ams::ldr {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience defines. */
|
||||
constexpr size_t SystemResourceSizeMax = 0x1FE00000;
|
||||
|
||||
/* Types. */
|
||||
enum NsoIndex {
|
||||
Nso_Rtld = 0,
|
||||
Nso_Main = 1,
|
||||
Nso_Compat0 = 2,
|
||||
Nso_Compat1 = 3,
|
||||
Nso_Compat2 = 4,
|
||||
Nso_Compat3 = 5,
|
||||
Nso_Compat4 = 6,
|
||||
Nso_Compat5 = 7,
|
||||
Nso_Compat6 = 8,
|
||||
Nso_Compat7 = 9,
|
||||
Nso_Compat8 = 10,
|
||||
Nso_Compat9 = 11,
|
||||
Nso_SubSdk0 = 12,
|
||||
Nso_SubSdk1 = 13,
|
||||
Nso_SubSdk2 = 14,
|
||||
Nso_SubSdk3 = 15,
|
||||
Nso_SubSdk4 = 16,
|
||||
Nso_SubSdk5 = 17,
|
||||
Nso_SubSdk6 = 18,
|
||||
Nso_SubSdk7 = 19,
|
||||
Nso_SubSdk8 = 20,
|
||||
Nso_SubSdk9 = 21,
|
||||
Nso_Sdk = 22,
|
||||
Nso_Count,
|
||||
};
|
||||
|
||||
constexpr inline const char *NsoPaths[Nso_Count] = {
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/rtld"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/main"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat0"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat1"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat2"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat3"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat4"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat5"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat6"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat7"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat8"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat9"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk0"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk1"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk2"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk3"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk4"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk5"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk6"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk7"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk8"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk9"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/sdk"),
|
||||
};
|
||||
|
||||
constexpr const char *GetNsoPath(size_t idx) {
|
||||
AMS_ABORT_UNLESS(idx < Nso_Count);
|
||||
return NsoPaths[idx];
|
||||
}
|
||||
|
||||
struct ProcessInfo {
|
||||
os::NativeHandle process_handle;
|
||||
uintptr_t args_address;
|
||||
size_t args_size;
|
||||
uintptr_t nso_address[Nso_Count];
|
||||
size_t nso_size[Nso_Count];
|
||||
};
|
||||
|
||||
/* Global NSO header cache. */
|
||||
bool g_has_nso[Nso_Count];
|
||||
NsoHeader g_nso_headers[Nso_Count];
|
||||
|
||||
/* Pcv/Ptm check cache */
|
||||
bool g_is_pcv;
|
||||
bool g_is_ptm;
|
||||
|
||||
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
|
||||
/* No version verification is done before 8.1.0. */
|
||||
R_SUCCEED_IF(hos::GetVersion() < hos::Version_8_1_0);
|
||||
|
||||
/* No verification is done if development. */
|
||||
R_SUCCEED_IF(IsDevelopmentForAntiDowngradeCheck());
|
||||
|
||||
/* TODO: Anti-downgrade checking does not make very much sense for us. Should we do anything? */
|
||||
AMS_UNUSED(program_id, version);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Helpers. */
|
||||
Result GetProgramInfoFromMeta(ProgramInfo *out, const Meta *meta) {
|
||||
/* Copy basic info. */
|
||||
out->main_thread_priority = meta->npdm->main_thread_priority;
|
||||
out->default_cpu_id = meta->npdm->default_cpu_id;
|
||||
out->main_thread_stack_size = meta->npdm->main_thread_stack_size;
|
||||
out->program_id = meta->aci->program_id;
|
||||
|
||||
/* Copy access controls. */
|
||||
size_t offset = 0;
|
||||
#define COPY_ACCESS_CONTROL(source, which) \
|
||||
({ \
|
||||
const size_t size = meta->source->which##_size; \
|
||||
R_UNLESS(offset + size <= sizeof(out->ac_buffer), ldr::ResultInternalError()); \
|
||||
out->source##_##which##_size = size; \
|
||||
std::memcpy(out->ac_buffer + offset, meta->source##_##which, size); \
|
||||
offset += size; \
|
||||
})
|
||||
|
||||
/* Copy all access controls to buffer. */
|
||||
COPY_ACCESS_CONTROL(acid, sac);
|
||||
COPY_ACCESS_CONTROL(aci, sac);
|
||||
COPY_ACCESS_CONTROL(acid, fac);
|
||||
COPY_ACCESS_CONTROL(aci, fah);
|
||||
#undef COPY_ACCESS_CONTROL
|
||||
|
||||
/* Copy flags. */
|
||||
out->flags = MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
bool IsApplet(const Meta *meta) {
|
||||
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet;
|
||||
}
|
||||
|
||||
bool IsApplication(const Meta *meta) {
|
||||
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Application;
|
||||
}
|
||||
|
||||
Npdm::AddressSpaceType GetAddressSpaceType(const Meta *meta) {
|
||||
return static_cast<Npdm::AddressSpaceType>((meta->npdm->flags & Npdm::MetaFlag_AddressSpaceTypeMask) >> Npdm::MetaFlag_AddressSpaceTypeShift);
|
||||
}
|
||||
|
||||
Acid::PoolPartition GetPoolPartition(const Meta *meta) {
|
||||
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
|
||||
}
|
||||
|
||||
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, bool *has_nso) {
|
||||
/* Clear NSOs. */
|
||||
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
|
||||
std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count);
|
||||
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
fs::FileHandle file;
|
||||
if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read))) {
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
/* Read NSO header. */
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, nso_headers + i, sizeof(*nso_headers)));
|
||||
R_UNLESS(read_size == sizeof(*nso_headers), ldr::ResultInvalidNso());
|
||||
|
||||
has_nso[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckAutoLoad(const NsoHeader *nso_headers, const bool *has_nso) {
|
||||
/* We must always have a main. */
|
||||
R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso());
|
||||
|
||||
/* If we don't have an RTLD, we must only have a main. */
|
||||
if (!has_nso[Nso_Rtld]) {
|
||||
for (size_t i = Nso_Main + 1; i < Nso_Count; i++) {
|
||||
R_UNLESS(!has_nso[i], ldr::ResultInvalidNso());
|
||||
}
|
||||
}
|
||||
|
||||
/* All NSOs must have zero text offset. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
R_UNLESS(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
constexpr const ncm::ProgramId UnqualifiedApprovalProgramIds[] = {
|
||||
{ 0x010003F003A34000 }, /* Pokemon: Let's Go, Pikachu! */
|
||||
{ 0x0100152000022000 }, /* Mario Kart 8 Deluxe */
|
||||
{ 0x0100165003504000 }, /* Nintendo Labo Toy-Con 04: VR Kit */
|
||||
{ 0x0100187003A36000 }, /* Pokemon: Let's Go, Eevee! */
|
||||
{ 0x01002E5008C56000 }, /* Pokemon Sword [Live Tournament] */
|
||||
{ 0x01002FF008C24000 }, /* Ring Fit Adventure */
|
||||
{ 0x010049900F546001 }, /* Super Mario 3D All-Stars: Super Mario 64 */
|
||||
{ 0x010057D00ECE4000 }, /* Nintendo Switch Online (Nintendo 64) [for Japan] */
|
||||
{ 0x01006F8002326000 }, /* Animal Crossing: New Horizons */
|
||||
{ 0x01006FB00F50E000 }, /* [???] */
|
||||
{ 0x010070300F50C000 }, /* [???] */
|
||||
{ 0x010075100E8EC000 }, /* 马力欧卡丁车8 豪华版 [Mario Kart 8 Deluxe for China] */
|
||||
{ 0x01008DB008C2C000 }, /* Pokemon Shield */
|
||||
{ 0x01009AD008C4C000 }, /* Pokemon: Let's Go, Pikachu! [Kiosk] */
|
||||
{ 0x0100A66003384000 }, /* Hulu */
|
||||
{ 0x0100ABF008968000 }, /* Pokemon Sword */
|
||||
{ 0x0100C9A00ECE6000 }, /* Nintendo Switch Online (Nintendo 64) [for America] */
|
||||
{ 0x0100ED100BA3A000 }, /* Mario Kart Live: Home Circuit */
|
||||
{ 0x0100F38011CFE000 }, /* Animal Crossing: New Horizons Island Transfer Tool */
|
||||
{ 0x0100F6B011028000 }, /* 健身环大冒险 [Ring Fit Adventure for China] */
|
||||
};
|
||||
|
||||
/* Check that the unqualified approval programs are sorted. */
|
||||
static_assert([]() -> bool {
|
||||
for (size_t i = 0; i < util::size(UnqualifiedApprovalProgramIds) - 1; ++i) {
|
||||
if (UnqualifiedApprovalProgramIds[i].value >= UnqualifiedApprovalProgramIds[i + 1].value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}());
|
||||
|
||||
bool IsUnqualifiedApprovalProgramId(ncm::ProgramId program_id) {
|
||||
/* Check if the program id is one with unqualified approval. */
|
||||
return std::binary_search(std::begin(UnqualifiedApprovalProgramIds), std::end(UnqualifiedApprovalProgramIds), program_id);
|
||||
}
|
||||
|
||||
bool IsUnqualifiedApproval(const Meta *meta) {
|
||||
/* If the meta has unqualified approval flag, it's unqualified approval. */
|
||||
if (meta->acid->flags & ldr::Acid::AcidFlag_UnqualifiedApproval) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the unqualified approval flag is not set, the program must be an application. */
|
||||
if (!IsApplication(meta)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The program id must be a force unqualified approval program id. */
|
||||
return IsUnqualifiedApprovalProgramId(meta->acid->program_id_min) && meta->acid->program_id_min == meta->acid->program_id_max;
|
||||
}
|
||||
|
||||
Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeVerificationData &code_verification_data) {
|
||||
/* Validate version. */
|
||||
R_TRY(ValidateProgramVersion(loc.program_id, meta->npdm->version));
|
||||
|
||||
/* Validate program id. */
|
||||
R_UNLESS(meta->aci->program_id >= meta->acid->program_id_min, ldr::ResultInvalidProgramId());
|
||||
R_UNLESS(meta->aci->program_id <= meta->acid->program_id_max, ldr::ResultInvalidProgramId());
|
||||
|
||||
/* Validate the kernel capabilities. */
|
||||
R_TRY(TestCapability(static_cast<const util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)));
|
||||
|
||||
/* Check if NCA is PCV or PTM */
|
||||
g_is_pcv = meta->aci->program_id == ncm::SystemProgramId::Pcv;
|
||||
g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
|
||||
|
||||
|
||||
/* If we have data to validate, validate it. */
|
||||
if (meta->check_verification_data) {
|
||||
const u8 *sig = code_verification_data.signature;
|
||||
const size_t sig_size = sizeof(code_verification_data.signature);
|
||||
const u8 *mod = static_cast<u8 *>(meta->modulus);
|
||||
const size_t mod_size = crypto::Rsa2048PssSha256Verifier::ModulusSize;
|
||||
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
|
||||
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
|
||||
const u8 *hsh = code_verification_data.target_hash;
|
||||
const size_t hsh_size = sizeof(code_verification_data.target_hash);
|
||||
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size);
|
||||
|
||||
/* If the signature check fails, we need to check if this is allowable. */
|
||||
if (!is_signature_valid) {
|
||||
/* We have to enforce signature checks on prod and when we have a signature to check on dev. */
|
||||
R_UNLESS(IsDevelopmentForAcidProductionCheck(), ldr::ResultInvalidNcaSignature());
|
||||
R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature());
|
||||
|
||||
/* There was no signature to check on dev. Check if this is acceptable. */
|
||||
R_UNLESS(IsUnqualifiedApproval(meta), ldr::ResultInvalidNcaSignature());
|
||||
}
|
||||
}
|
||||
|
||||
/* All good. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
|
||||
const u8 meta_flags = meta->npdm->flags;
|
||||
|
||||
u32 flags = 0;
|
||||
|
||||
/* Set Is64Bit. */
|
||||
if (meta_flags & Npdm::MetaFlag_Is64Bit) {
|
||||
flags |= svc::CreateProcessFlag_Is64Bit;
|
||||
}
|
||||
|
||||
/* Set AddressSpaceType. */
|
||||
switch (GetAddressSpaceType(meta)) {
|
||||
case Npdm::AddressSpaceType_32Bit:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace32Bit;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_64BitDeprecated:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace64BitDeprecated;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_32BitWithoutAlias:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace32BitWithoutAlias;
|
||||
break;
|
||||
case Npdm::AddressSpaceType_64Bit:
|
||||
flags |= svc::CreateProcessFlag_AddressSpace64Bit;
|
||||
break;
|
||||
default:
|
||||
R_THROW(ldr::ResultInvalidMeta());
|
||||
}
|
||||
|
||||
/* Set Enable Debug. */
|
||||
if (ldr_flags & CreateProcessFlag_EnableDebug) {
|
||||
flags |= svc::CreateProcessFlag_EnableDebug;
|
||||
}
|
||||
|
||||
/* Set Enable ASLR. */
|
||||
if (!(ldr_flags & CreateProcessFlag_DisableAslr)) {
|
||||
flags |= svc::CreateProcessFlag_EnableAslr;
|
||||
}
|
||||
|
||||
/* Set Is Application. */
|
||||
if (IsApplication(meta)) {
|
||||
flags |= svc::CreateProcessFlag_IsApplication;
|
||||
|
||||
/* 7.0.0+: Set OptimizeMemoryAllocation if relevant. */
|
||||
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
||||
if (meta_flags & Npdm::MetaFlag_OptimizeMemoryAllocation) {
|
||||
flags |= svc::CreateProcessFlag_OptimizeMemoryAllocation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 5.0.0+ Set Pool Partition. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
/* TODO: Nintendo no longer accepts Applet when pool partition == application. Would this break hbl/anything else in the hb ecosystem? */
|
||||
/* TODO: Nintendo uses a helper bool MakeSvcPoolPartitionFlag(u32 *out, Acid::PoolPartition partition); */
|
||||
switch (GetPoolPartition(meta)) {
|
||||
case Acid::PoolPartition_Application:
|
||||
if (IsApplet(meta)) {
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||
} else {
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplication;
|
||||
}
|
||||
break;
|
||||
case Acid::PoolPartition_Applet:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||
break;
|
||||
case Acid::PoolPartition_System:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionSystem;
|
||||
break;
|
||||
case Acid::PoolPartition_SystemNonSecure:
|
||||
flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure;
|
||||
break;
|
||||
default:
|
||||
R_THROW(ldr::ResultInvalidMeta());
|
||||
}
|
||||
} else if (hos::GetVersion() >= hos::Version_4_0_0) {
|
||||
/* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */
|
||||
if (meta->acid->flags & Acid::AcidFlag_DeprecatedUseSecureMemory) {
|
||||
flags |= svc::CreateProcessFlag_DeprecatedUseSecureMemory;
|
||||
}
|
||||
}
|
||||
|
||||
/* 11.0.0+/meso Set Disable DAS merge. */
|
||||
if (meta_flags & Npdm::MetaFlag_DisableDeviceAddressSpaceMerge) {
|
||||
flags |= svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge;
|
||||
}
|
||||
|
||||
/* 18.0.0+/meso Set Alias region extra size. */
|
||||
if (meta_flags & Npdm::MetaFlag_EnableAliasRegionExtraSize) {
|
||||
flags |= svc::CreateProcessFlag_EnableAliasRegionExtraSize;
|
||||
}
|
||||
|
||||
*out = flags;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetCreateProcessParameter(svc::CreateProcessParameter *out, const Meta *meta, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Clear output. */
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
|
||||
/* Set name, version, program id, resource limit handle. */
|
||||
std::memcpy(out->name, meta->npdm->program_name, sizeof(out->name) - 1);
|
||||
out->version = meta->npdm->version;
|
||||
out->program_id = meta->aci->program_id.value;
|
||||
out->reslimit = resource_limit;
|
||||
|
||||
/* Set flags. */
|
||||
R_TRY(GetCreateProcessFlags(std::addressof(out->flags), meta, flags));
|
||||
|
||||
/* 3.0.0+ System Resource Size. */
|
||||
if (hos::GetVersion() >= hos::Version_3_0_0) {
|
||||
/* Validate size is aligned. */
|
||||
R_UNLESS(util::IsAligned(meta->npdm->system_resource_size, os::MemoryBlockUnitSize), ldr::ResultInvalidSize());
|
||||
|
||||
/* Validate system resource usage. */
|
||||
if (meta->npdm->system_resource_size) {
|
||||
/* Process must be 64-bit. */
|
||||
R_UNLESS((out->flags & svc::CreateProcessFlag_AddressSpace64Bit), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Process must be application or applet. */
|
||||
R_UNLESS(IsApplication(meta) || IsApplet(meta), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Size must be less than or equal to max. */
|
||||
R_UNLESS(meta->npdm->system_resource_size <= SystemResourceSizeMax, ldr::ResultInvalidMeta());
|
||||
}
|
||||
out->system_resource_num_pages = meta->npdm->system_resource_size >> 12;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u64 GenerateSecureRandom(u64 max) {
|
||||
/* Generate a cryptographically random number. */
|
||||
u64 rand;
|
||||
crypto::GenerateCryptographicallyRandomBytes(std::addressof(rand), sizeof(rand));
|
||||
|
||||
/* Coerce into range. */
|
||||
return rand % (max + 1);
|
||||
}
|
||||
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) {
|
||||
/* Clear output. */
|
||||
out->args_address = 0;
|
||||
out->args_size = 0;
|
||||
std::memset(out->nso_address, 0, sizeof(out->nso_address));
|
||||
std::memset(out->nso_size, 0, sizeof(out->nso_size));
|
||||
|
||||
size_t total_size = 0;
|
||||
bool argument_allocated = false;
|
||||
|
||||
/* Calculate base offsets. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (has_nso[i]) {
|
||||
out->nso_address[i] = total_size;
|
||||
const size_t text_end = nso_headers[i].text_dst_offset + nso_headers[i].text_size;
|
||||
const size_t ro_end = nso_headers[i].ro_dst_offset + nso_headers[i].ro_size;
|
||||
const size_t rw_end = nso_headers[i].rw_dst_offset + nso_headers[i].rw_size + nso_headers[i].bss_size;
|
||||
out->nso_size[i] = text_end;
|
||||
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
|
||||
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
|
||||
out->nso_size[i] = util::AlignUp(out->nso_size[i], os::MemoryPageSize);
|
||||
|
||||
total_size += out->nso_size[i];
|
||||
|
||||
if (!argument_allocated && argument != nullptr) {
|
||||
out->args_address = total_size;
|
||||
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
|
||||
total_size += out->args_size;
|
||||
argument_allocated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate ASLR. */
|
||||
uintptr_t aslr_start = 0;
|
||||
size_t aslr_size = 0;
|
||||
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
||||
switch (out_param->flags & svc::CreateProcessFlag_AddressSpaceMask) {
|
||||
case svc::CreateProcessFlag_AddressSpace32Bit:
|
||||
case svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
|
||||
aslr_start = svc::AddressSmallMap32Start;
|
||||
aslr_size = svc::AddressSmallMap32Size;
|
||||
break;
|
||||
case svc::CreateProcessFlag_AddressSpace64BitDeprecated:
|
||||
aslr_start = svc::AddressSmallMap36Start;
|
||||
aslr_size = svc::AddressSmallMap36Size;
|
||||
break;
|
||||
case svc::CreateProcessFlag_AddressSpace64Bit:
|
||||
aslr_start = svc::AddressMap39Start;
|
||||
aslr_size = svc::AddressMap39Size;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
} else {
|
||||
/* On 1.0.0, only 2 address space types existed. */
|
||||
if (out_param->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) {
|
||||
aslr_start = svc::AddressSmallMap36Start;
|
||||
aslr_size = svc::AddressSmallMap36Size;
|
||||
} else {
|
||||
aslr_start = svc::AddressSmallMap32Start;
|
||||
aslr_size = svc::AddressSmallMap32Size;
|
||||
}
|
||||
}
|
||||
R_UNLESS(total_size <= aslr_size, svc::ResultOutOfMemory());
|
||||
|
||||
/* Set Create Process output. */
|
||||
uintptr_t aslr_slide = 0;
|
||||
size_t free_size = (aslr_size - total_size);
|
||||
if (out_param->flags & svc::CreateProcessFlag_EnableAslr) {
|
||||
aslr_slide = GenerateSecureRandom(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize;
|
||||
}
|
||||
|
||||
/* Set out. */
|
||||
aslr_start += aslr_slide;
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (has_nso[i]) {
|
||||
out->nso_address[i] += aslr_start;
|
||||
}
|
||||
}
|
||||
if (out->args_address) {
|
||||
out->args_address += aslr_start;
|
||||
}
|
||||
|
||||
out_param->code_address = aslr_start;
|
||||
out_param->code_num_pages = total_size >> 12;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModuleSegment(fs::FileHandle file, const NsoHeader::SegmentInfo *segment, size_t file_size, const u8 *file_hash, bool is_compressed, bool check_hash, uintptr_t map_base, uintptr_t map_end) {
|
||||
/* Select read size based on compression. */
|
||||
if (!is_compressed) {
|
||||
file_size = segment->size;
|
||||
}
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(file_size <= segment->size, ldr::ResultInvalidNso());
|
||||
R_UNLESS(segment->size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
|
||||
/* Load data from file. */
|
||||
uintptr_t load_address = is_compressed ? map_end - file_size : map_base;
|
||||
size_t read_size;
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, segment->file_offset, reinterpret_cast<void *>(load_address), file_size));
|
||||
R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
|
||||
|
||||
/* Uncompress if necessary. */
|
||||
if (is_compressed) {
|
||||
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment->size, reinterpret_cast<const void *>(load_address), file_size) == static_cast<int>(segment->size));
|
||||
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* Check hash if necessary. */
|
||||
if (check_hash) {
|
||||
u8 hash[crypto::Sha256Generator::HashSize];
|
||||
crypto::GenerateSha256(hash, sizeof(hash), reinterpret_cast<void *>(map_base), segment->size);
|
||||
|
||||
R_UNLESS(std::memcmp(hash, file_hash, sizeof(hash)) == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size, bool prevent_code_reads) {
|
||||
/* Map and read data from file. */
|
||||
{
|
||||
/* Map the process memory. */
|
||||
void *mapped_memory = nullptr;
|
||||
R_TRY(os::MapProcessMemory(std::addressof(mapped_memory), process_handle, nso_address, nso_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); };
|
||||
|
||||
const uintptr_t map_address = reinterpret_cast<uintptr_t>(mapped_memory);
|
||||
|
||||
/* Load NSO segments. */
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Text]), nso_header->text_compressed_size, nso_header->text_hash, (nso_header->flags & NsoHeader::Flag_CompressedText) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashText) != 0, map_address + nso_header->text_dst_offset, map_address + nso_size));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Ro]), nso_header->ro_compressed_size, nso_header->ro_hash, (nso_header->flags & NsoHeader::Flag_CompressedRo) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRo) != 0, map_address + nso_header->ro_dst_offset, map_address + nso_size));
|
||||
R_TRY(LoadAutoLoadModuleSegment(file, std::addressof(nso_header->segments[NsoHeader::Segment_Rw]), nso_header->rw_compressed_size, nso_header->rw_hash, (nso_header->flags & NsoHeader::Flag_CompressedRw) != 0,
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
|
||||
|
||||
/* Clear unused space to zero. */
|
||||
const size_t text_end = nso_header->text_dst_offset + nso_header->text_size;
|
||||
const size_t ro_end = nso_header->ro_dst_offset + nso_header->ro_size;
|
||||
const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size;
|
||||
std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
|
||||
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + rw_end), 0, nso_header->bss_size);
|
||||
|
||||
/* Apply embedded patches. */
|
||||
ApplyEmbeddedPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* Apply IPS patches. */
|
||||
LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* Apply PCV and PTM patches */
|
||||
if (g_is_pcv)
|
||||
hoc::pcv::Patch(map_address, nso_size);
|
||||
if (g_is_ptm)
|
||||
hoc::ptm::Patch(map_address, nso_size);
|
||||
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (text_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute));
|
||||
}
|
||||
if (ro_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->ro_dst_offset, ro_size, os::MemoryPermission_ReadOnly));
|
||||
}
|
||||
if (rw_size) {
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->rw_dst_offset, rw_size, os::MemoryPermission_ReadWrite));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, bool prevent_code_reads) {
|
||||
/* Load each NSO. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (has_nso[i]) {
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i], prevent_code_reads));
|
||||
}
|
||||
}
|
||||
|
||||
/* Load arguments, if present. */
|
||||
if (argument != nullptr) {
|
||||
/* Write argument data into memory. */
|
||||
{
|
||||
void *map_address = nullptr;
|
||||
R_TRY(os::MapProcessMemory(std::addressof(map_address), process_info->process_handle, process_info->args_address, process_info->args_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(map_address, process_info->process_handle, process_info->args_address, process_info->args_size); };
|
||||
|
||||
ProgramArguments *args = static_cast<ProgramArguments *>(map_address);
|
||||
std::memset(args, 0, sizeof(*args));
|
||||
args->allocated_size = process_info->args_size;
|
||||
args->arguments_size = argument->argument_size;
|
||||
std::memcpy(args->arguments, argument->argument, argument->argument_size);
|
||||
}
|
||||
|
||||
/* Set argument region permissions. */
|
||||
/* NOTE: Nintendo uses svc::SetProcessMemoryPermission directly here. */
|
||||
R_TRY(os::SetProcessMemoryPermission(process_info->process_handle, process_info->args_address, process_info->args_size, os::MemoryPermission_ReadWrite));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Get CreateProcessParameter. */
|
||||
svc::CreateProcessParameter param;
|
||||
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
||||
|
||||
/* Decide on an NSO layout. */
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument));
|
||||
|
||||
/* Actually create process. */
|
||||
svc::Handle process_handle;
|
||||
R_TRY(svc::CreateProcess(std::addressof(process_handle), std::addressof(param), static_cast<const u32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(u32)));
|
||||
|
||||
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
|
||||
out->process_handle = process_handle;
|
||||
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
||||
|
||||
/* Load all auto load modules. */
|
||||
R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument, (meta->npdm->flags & ldr::Npdm::MetaFlag_PreventCodeReads) != 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Process Creation API. */
|
||||
Result CreateProcess(os::NativeHandle *out, PinId pin_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status, const char *path, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit, const ldr::ProgramAttributes &attrs) {
|
||||
/* Mount code. */
|
||||
AMS_UNUSED(path);
|
||||
ScopedCodeMount mount(loc, override_status, attrs);
|
||||
R_TRY(mount.GetResult());
|
||||
|
||||
/* Load meta, possibly from cache. */
|
||||
Meta meta;
|
||||
R_TRY(LoadMetaFromCache(std::addressof(meta), loc, override_status, attrs.platform));
|
||||
|
||||
/* Validate meta. */
|
||||
R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData()));
|
||||
|
||||
/* Load, validate NSO headers. */
|
||||
R_TRY(LoadAutoLoadHeaders(g_nso_headers, g_has_nso));
|
||||
R_TRY(CheckAutoLoad(g_nso_headers, g_has_nso));
|
||||
|
||||
/* Actually create the process and load NSOs into process memory. */
|
||||
ProcessInfo info;
|
||||
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit));
|
||||
|
||||
/* Register NSOs with the RoManager. */
|
||||
{
|
||||
/* Nintendo doesn't validate this get, but we do. */
|
||||
os::ProcessId process_id = os::GetProcessId(info.process_handle);
|
||||
|
||||
/* Register new process. */
|
||||
const auto as_type = GetAddressSpaceType(std::addressof(meta));
|
||||
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::AddressSpaceType_64Bit || as_type == Npdm::AddressSpaceType_64BitDeprecated);
|
||||
|
||||
/* Register all NSOs. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (g_has_nso[i]) {
|
||||
RoManager::GetInstance().AddNso(pin_id, g_nso_headers[i].module_id, info.nso_address[i], info.nso_size[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're overriding for HBL, perform HTML document redirection. */
|
||||
if (override_status.IsHbl()) {
|
||||
/* Don't validate result, failure is okay. */
|
||||
RedirectHtmlDocumentPathForHbl(loc);
|
||||
}
|
||||
|
||||
/* Clear the external code for the program. */
|
||||
fssystem::DestroyExternalCode(loc.program_id);
|
||||
|
||||
/* Note that we've created the program. */
|
||||
SetLaunchedBootProgram(loc.program_id);
|
||||
|
||||
/* Move the process handle to output. */
|
||||
*out = info.process_handle;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProgramInfo(ProgramInfo *out, cfg::OverrideStatus *out_status, const ncm::ProgramLocation &loc, const char *path, const ldr::ProgramAttributes &attrs) {
|
||||
Meta meta;
|
||||
|
||||
/* Load Meta. */
|
||||
{
|
||||
AMS_UNUSED(path);
|
||||
|
||||
ScopedCodeMount mount(loc, attrs);
|
||||
R_TRY(mount.GetResult());
|
||||
R_TRY(LoadMeta(std::addressof(meta), loc, mount.GetOverrideStatus(), attrs.platform, false));
|
||||
if (out_status != nullptr) {
|
||||
*out_status = mount.GetOverrideStatus();
|
||||
}
|
||||
}
|
||||
|
||||
return GetProgramInfoFromMeta(out, std::addressof(meta));
|
||||
}
|
||||
|
||||
Result PinProgram(PinId *out_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status) {
|
||||
R_UNLESS(RoManager::GetInstance().Allocate(out_id, loc, override_status), ldr::ResultMaxProcess());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result UnpinProgram(PinId id) {
|
||||
R_UNLESS(RoManager::GetInstance().Free(id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProcessModuleInfo(u32 *out_count, ldr::ModuleInfo *out, size_t max_out_count, os::ProcessId process_id) {
|
||||
R_UNLESS(RoManager::GetInstance().GetProcessModuleInfo(out_count, out, max_out_count, process_id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetProgramLocationAndOverrideStatusFromPinId(ncm::ProgramLocation *out, cfg::OverrideStatus *out_status, PinId pin_id) {
|
||||
R_UNLESS(RoManager::GetInstance().GetProgramLocationAndStatus(out, out_status, pin_id), ldr::ResultNotPinned());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
** FOR DEVELOPERS UTILIZING SYSCLK API ONLY **
|
||||
|
||||
Ensure you include the latest hoc-clk ipc and header files in your project before proceeding
|
||||
|
||||
Before running migration replacements, change every reference to sys-clk's ramload api to this
|
||||
ramLoad -> partLoad
|
||||
SysClkRamLoad_All -> HocClkPartLoad_EMC
|
||||
SysClkRamLoad_Cpu -> HocClkPartLoad_EMCCpu
|
||||
|
||||
API version reference must be changed. compare to HOCCLK_IPC_API_VERSION
|
||||
If you use the service name, use HOCCLK_IPC_SERVICE_NAME
|
||||
|
||||
Remove checks for the u8 enabled in sysclk clockmanager struct. Check if hocclk is enabled by listening to IPC results
|
||||
|
||||
Run the following replace commands (case sensitive):
|
||||
|
||||
sysclk -> hocclk
|
||||
SysClk -> HocClk
|
||||
SYSCLK -> HOCCLK
|
||||
sysClk -> hocClk
|
||||
|
||||
Your project is now migrated to run with HOC
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) MasaGratoR
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "ipc.h"
|
||||
|
||||
Handle saltysd_orig;
|
||||
|
||||
Result SaltySD_Connect() {
|
||||
for (int i = 0; i < 200; i++) {
|
||||
if (!svcConnectToNamedPort(&saltysd_orig, "SaltySD"))
|
||||
return 0;
|
||||
svcSleepThread(1000*1000);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
Result SaltySD_Term()
|
||||
{
|
||||
Result ret;
|
||||
IpcCommand c;
|
||||
|
||||
ipcInitialize(&c);
|
||||
ipcSendPid(&c);
|
||||
|
||||
struct input
|
||||
{
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 zero;
|
||||
u64 reserved[2];
|
||||
} *raw;
|
||||
|
||||
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 0;
|
||||
raw->zero = 0;
|
||||
|
||||
ret = ipcDispatch(saltysd_orig);
|
||||
|
||||
if (R_SUCCEEDED(ret))
|
||||
{
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct output {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *resp = (output*)r.Raw;
|
||||
|
||||
ret = resp->result;
|
||||
}
|
||||
|
||||
// Session terminated works too.
|
||||
svcCloseHandle(saltysd_orig);
|
||||
if (ret == 0xf601) return 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result SaltySD_CheckIfSharedMemoryAvailable(ptrdiff_t *offset, u64 size)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
// Send a command
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcSendPid(&c);
|
||||
|
||||
struct input {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 size;
|
||||
u32 reserved[2];
|
||||
} *raw;
|
||||
|
||||
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 6;
|
||||
raw->size = size;
|
||||
|
||||
ret = ipcDispatch(saltysd_orig);
|
||||
|
||||
if (R_SUCCEEDED(ret)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct output {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u64 offset;
|
||||
} *resp = (output*)r.Raw;
|
||||
|
||||
ret = resp->result;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
*offset = resp->offset;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result SaltySD_GetSharedMemoryHandle(Handle *retrieve)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
// Send a command
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcSendPid(&c);
|
||||
|
||||
struct input {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u32 reserved[4];
|
||||
} *raw;
|
||||
|
||||
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 7;
|
||||
|
||||
ret = ipcDispatch(saltysd_orig);
|
||||
|
||||
if (R_SUCCEEDED(ret)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct output {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u64 reserved[2];
|
||||
} *resp = (output*)r.Raw;
|
||||
|
||||
ret = resp->result;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
*retrieve = r.Handles[0];
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result SaltySD_GetDisplayRefreshRate(uint8_t* refreshRate)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
// Send a command
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcSendPid(&c);
|
||||
|
||||
struct input {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 zero;
|
||||
u64 reserved;
|
||||
} *raw;
|
||||
|
||||
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 10;
|
||||
raw->zero = 0;
|
||||
|
||||
ret = ipcDispatch(saltysd_orig);
|
||||
|
||||
if (R_SUCCEEDED(ret)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct output {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u64 refreshRate;
|
||||
u64 reserved;
|
||||
} *resp = (output*)r.Raw;
|
||||
|
||||
ret = resp->result;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
*refreshRate = (uint8_t)(resp->refreshRate);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Result SaltySD_SetDisplayRefreshRate(uint8_t refreshRate)
|
||||
{
|
||||
Result ret = 0;
|
||||
|
||||
// Send a command
|
||||
IpcCommand c;
|
||||
ipcInitialize(&c);
|
||||
ipcSendPid(&c);
|
||||
|
||||
struct input {
|
||||
u64 magic;
|
||||
u64 cmd_id;
|
||||
u64 refreshRate;
|
||||
u64 reserved;
|
||||
} *raw;
|
||||
|
||||
raw = (input*)ipcPrepareHeader(&c, sizeof(*raw));
|
||||
|
||||
raw->magic = SFCI_MAGIC;
|
||||
raw->cmd_id = 11;
|
||||
raw->refreshRate = refreshRate;
|
||||
|
||||
ret = ipcDispatch(saltysd_orig);
|
||||
|
||||
if (R_SUCCEEDED(ret)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct output {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u64 reserved[2];
|
||||
} *resp = (output*)r.Raw;
|
||||
|
||||
ret = resp->result;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
typedef enum {
|
||||
BatteryFlag_NoHub = BIT(0), // Hub is disconnected
|
||||
BatteryFlag_Rail = BIT(8), // At least one Joy-con is charging from rail
|
||||
BatteryFlag_SPDSRC = BIT(12), // OTG
|
||||
BatteryFlag_ACC = BIT(16) // Accessory
|
||||
} BatteryChargeFlags;
|
||||
|
||||
typedef enum {
|
||||
PDState_NewPDO = 1, // Received new Power Data Object
|
||||
PDState_NoPD = 2, // No Power Delivery source is detected
|
||||
PDState_AcceptedRDO = 3 // Received and accepted Request Data Object
|
||||
} BatteryPDControllerState;
|
||||
|
||||
// Charger type detection
|
||||
typedef enum {
|
||||
ChargerType_None = 0,
|
||||
ChargerType_PD = 1,
|
||||
ChargerType_TypeC_1500mA = 2,
|
||||
ChargerType_TypeC_3000mA = 3,
|
||||
ChargerType_DCP = 4, // Dedicated Charging Port
|
||||
ChargerType_CDP = 5, // Charging Downstream Port
|
||||
ChargerType_SDP = 6, // Standard Downstream Port
|
||||
ChargerType_Apple_500mA = 7,
|
||||
ChargerType_Apple_1000mA = 8,
|
||||
ChargerType_Apple_2000mA = 9
|
||||
} BatteryChargerType;
|
||||
typedef enum {
|
||||
PowerRole_Sink = 1, // Device is receiving power
|
||||
PowerRole_Source = 2 // Device is providing power
|
||||
} BatteryPowerRole;
|
||||
|
||||
typedef struct {
|
||||
int32_t InputCurrentLimit; // Input (Sink) current limit in mA
|
||||
int32_t VBUSCurrentLimit; // Output (Source/VBUS/OTG) current limit in mA
|
||||
int32_t ChargeCurrentLimit; // Battery charging current limit in mA
|
||||
int32_t ChargeVoltageLimit; // Battery charging voltage limit in mV
|
||||
int32_t unk_x10; // Unknown field (possibly enum)
|
||||
int32_t unk_x14; // Unknown field (possibly flags)
|
||||
BatteryPDControllerState PDControllerState; // PD Controller State
|
||||
int32_t BatteryTemperature; // Battery temperature in milli-Celsius
|
||||
int32_t RawBatteryCharge; // Battery charge in percentmille
|
||||
int32_t VoltageAvg; // Average voltage in mV
|
||||
int32_t BatteryAge; // Battery health (capacity full/design) in pcm
|
||||
BatteryPowerRole PowerRole; // Current power role
|
||||
BatteryChargerType ChargerType; // Type of charger connected
|
||||
int32_t ChargerVoltageLimit; // Charger voltage limit in mV
|
||||
int32_t ChargerCurrentLimit; // Charger current limit in mA
|
||||
BatteryChargeFlags Flags; // Various status flags
|
||||
} BatteryChargeInfo;
|
||||
|
||||
#define IS_BATTERY_CHARGING_ENABLED(info) (((info)->unk_x14 >> 8) & 1)
|
||||
|
||||
static inline int batteryInfoGetTemperatureMiliCelsius(BatteryChargeInfo *info) {
|
||||
return info->BatteryTemperature;
|
||||
}
|
||||
|
||||
static inline float batteryInfoGetChargePercent(BatteryChargeInfo *info) {
|
||||
return (float)info->RawBatteryCharge / 1000.0f;
|
||||
}
|
||||
|
||||
static inline float batteryInfoGetBatteryHealthPercent(BatteryChargeInfo *info) {
|
||||
return (float)info->BatteryAge / 1000.0f;
|
||||
}
|
||||
|
||||
static inline bool batteryInfoIsCharging(BatteryChargeInfo *info) {
|
||||
return IS_BATTERY_CHARGING_ENABLED(info);
|
||||
}
|
||||
|
||||
Result batteryInfoInitialize(void);
|
||||
void batteryInfoExit(void);
|
||||
Result batteryInfoGetChargeInfo(BatteryChargeInfo *out);
|
||||
Result batteryInfoGetChargePercentage(u32 *out);
|
||||
Result batteryInfoIsEnoughPowerSupplied(bool *out);
|
||||
Result batteryInfoEnableCharging(void);
|
||||
Result batteryInfoDisableCharging(void);
|
||||
Result batteryInfoEnableFastCharging(void);
|
||||
Result batteryInfoDisableFastCharging(void);
|
||||
const char* batteryInfoGetChargerTypeString(BatteryChargerType type);
|
||||
const char* batteryInfoGetPowerRoleString(BatteryPowerRole role);
|
||||
const char* batteryInfoGetPDStateString(BatteryPDControllerState state);
|
||||
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018 shuffle2
|
||||
* Copyright (c) 2018 balika011
|
||||
* Copyright (c) 2019-2025 CTCaer
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _FUSE_H_
|
||||
#define _FUSE_H_
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(n) (1U<<(n))
|
||||
#endif
|
||||
|
||||
/*! Fuse registers. */
|
||||
#define FUSE_CTRL 0x0
|
||||
#define FUSE_ADDR 0x4
|
||||
#define FUSE_RDATA 0x8
|
||||
#define FUSE_WDATA 0xC
|
||||
#define FUSE_TIME_RD1 0x10
|
||||
#define FUSE_TIME_RD2 0x14
|
||||
#define FUSE_TIME_PGM1 0x18
|
||||
#define FUSE_TIME_PGM2 0x1C
|
||||
#define FUSE_PRIV2INTFC 0x20
|
||||
#define FUSE_PRIV2INTFC_START_DATA BIT(0)
|
||||
#define FUSE_PRIV2INTFC_SKIP_RECORDS BIT(1)
|
||||
#define FUSE_FUSEBYPASS 0x24
|
||||
#define FUSE_PRIVATEKEYDISABLE 0x28
|
||||
#define FUSE_PRIVKEY_DISABLE BIT(0)
|
||||
#define FUSE_PRIVKEY_TZ_STICKY_BIT BIT(4)
|
||||
#define FUSE_DISABLEREGPROGRAM 0x2C
|
||||
#define FUSE_WRITE_ACCESS_SW 0x30
|
||||
#define FUSE_PWR_GOOD_SW 0x34
|
||||
#define FUSE_PRIV2RESHIFT 0x3C
|
||||
#define FUSE_FUSETIME_RD0 0x40
|
||||
#define FUSE_FUSETIME_RD1 0x44
|
||||
#define FUSE_FUSETIME_RD2 0x48
|
||||
#define FUSE_FUSETIME_RD3 0x4C
|
||||
#define FUSE_PRIVATE_KEY0_NONZERO 0x80
|
||||
#define FUSE_PRIVATE_KEY1_NONZERO 0x84
|
||||
#define FUSE_PRIVATE_KEY2_NONZERO 0x88
|
||||
#define FUSE_PRIVATE_KEY3_NONZERO 0x8C
|
||||
#define FUSE_PRIVATE_KEY4_NONZERO 0x90
|
||||
|
||||
/*! Fuse Cached registers */
|
||||
#define FUSE_RESERVED_ODM8_B01 0x98 // FUSE_READ_TZ Group 0.
|
||||
#define FUSE_RESERVED_ODM9_B01 0x9C // FUSE_READ_TZ Group 0.
|
||||
#define FUSE_RESERVED_ODM10_B01 0xA0 // FUSE_READ_TZ Group 0.
|
||||
#define FUSE_RESERVED_ODM11_B01 0xA4 // FUSE_READ_TZ Group 0.
|
||||
#define FUSE_RESERVED_ODM12_B01 0xA8 // FUSE_READ_TZ Group 1? Is value -1?
|
||||
#define FUSE_RESERVED_ODM13_B01 0xAC // FUSE_READ_TZ Group 1? Is value -1?
|
||||
#define FUSE_RESERVED_ODM14_B01 0xB0 // FUSE_READ_TZ Group 1? Is value -1?
|
||||
#define FUSE_RESERVED_ODM15_B01 0xB4 // FUSE_READ_TZ Group 1? Is value -1?
|
||||
#define FUSE_RESERVED_ODM16_B01 0xB8 // FUSE_READ_TZ Group 2? Is value -1?
|
||||
#define FUSE_RESERVED_ODM17_B01 0xBC // FUSE_READ_TZ Group 2? Is value -1?
|
||||
#define FUSE_RESERVED_ODM18_B01 0xC0 // FUSE_READ_TZ Group 2.
|
||||
#define FUSE_RESERVED_ODM19_B01 0xC4 // FUSE_READ_TZ Group 2.
|
||||
#define FUSE_RESERVED_ODM20_B01 0xC8 // FUSE_READ_TZ Group 3.
|
||||
#define FUSE_RESERVED_ODM21_B01 0xCC // FUSE_READ_TZ Group 3.
|
||||
#define FUSE_KEK00_B01 0xD0
|
||||
#define FUSE_KEK01_B01 0xD4
|
||||
#define FUSE_KEK02_B01 0xD8
|
||||
#define FUSE_KEK03_B01 0xDC
|
||||
#define FUSE_BEK00_B01 0xE0
|
||||
#define FUSE_BEK01_B01 0xE4
|
||||
#define FUSE_BEK02_B01 0xE8
|
||||
#define FUSE_BEK03_B01 0xEC
|
||||
#define FUSE_OPT_RAM_RTSEL_TSMCSP_PO4SVT_B01 0xF0
|
||||
#define FUSE_OPT_RAM_WTSEL_TSMCSP_PO4SVT_B01 0xF4
|
||||
#define FUSE_OPT_RAM_RTSEL_TSMCPDP_PO4SVT_B01 0xF8
|
||||
#define FUSE_OPT_RAM_MTSEL_TSMCPDP_PO4SVT_B01 0xFC
|
||||
|
||||
#define FUSE_PRODUCTION_MODE 0x100
|
||||
#define FUSE_JTAG_SECUREID_VALID 0x104
|
||||
#define FUSE_ODM_LOCK 0x108
|
||||
#define FUSE_OPT_OPENGL_EN 0x10C
|
||||
#define FUSE_SKU_INFO 0x110
|
||||
#define FUSE_CPU_SPEEDO_0_CALIB 0x114
|
||||
#define FUSE_CPU_IDDQ_CALIB 0x118
|
||||
|
||||
#define FUSE_RESERVED_ODM22_B01 0x11C // FUSE_READ_TZ Group 3.
|
||||
#define FUSE_RESERVED_ODM23_B01 0x120 // FUSE_READ_TZ Group 3.
|
||||
#define FUSE_RESERVED_ODM24_B01 0x124 // FUSE_READ_TZ Group 4.
|
||||
|
||||
#define FUSE_OPT_FT_REV 0x128
|
||||
#define FUSE_CPU_SPEEDO_1_CALIB 0x12C
|
||||
#define FUSE_CPU_SPEEDO_2_CALIB 0x130
|
||||
#define FUSE_SOC_SPEEDO_0_CALIB 0x134
|
||||
#define FUSE_SOC_SPEEDO_1_CALIB 0x138
|
||||
#define FUSE_SOC_SPEEDO_2_CALIB 0x13C
|
||||
#define FUSE_SOC_IDDQ_CALIB 0x140
|
||||
|
||||
#define FUSE_RESERVED_ODM25_B01 0x144 // FUSE_READ_TZ Group 4.
|
||||
|
||||
#define FUSE_FA 0x148
|
||||
#define FUSE_RESERVED_PRODUCTION 0x14C
|
||||
#define FUSE_HDMI_LANE0_CALIB 0x150
|
||||
#define FUSE_HDMI_LANE1_CALIB 0x154
|
||||
#define FUSE_HDMI_LANE2_CALIB 0x158
|
||||
#define FUSE_HDMI_LANE3_CALIB 0x15C
|
||||
#define FUSE_ENCRYPTION_RATE 0x160
|
||||
#define FUSE_PUBLIC_KEY0 0x164
|
||||
#define FUSE_PUBLIC_KEY1 0x168
|
||||
#define FUSE_PUBLIC_KEY2 0x16C
|
||||
#define FUSE_PUBLIC_KEY3 0x170
|
||||
#define FUSE_PUBLIC_KEY4 0x174
|
||||
#define FUSE_PUBLIC_KEY5 0x178
|
||||
#define FUSE_PUBLIC_KEY6 0x17C
|
||||
#define FUSE_PUBLIC_KEY7 0x180
|
||||
#define FUSE_TSENSOR1_CALIB 0x184 // CPU1.
|
||||
#define FUSE_TSENSOR2_CALIB 0x188 // CPU2.
|
||||
|
||||
#define FUSE_OPT_SECURE_SCC_DIS_B01 0x18C
|
||||
|
||||
#define FUSE_OPT_CP_REV 0x190 // FUSE style revision - ATE. 0x101 0x100
|
||||
#define FUSE_OPT_PFG 0x194
|
||||
#define FUSE_TSENSOR0_CALIB 0x198 // CPU0.
|
||||
#define FUSE_FIRST_BOOTROM_PATCH_SIZE 0x19C
|
||||
#define FUSE_SECURITY_MODE 0x1A0
|
||||
#define FUSE_PRIVATE_KEY0 0x1A4
|
||||
#define FUSE_PRIVATE_KEY1 0x1A8
|
||||
#define FUSE_PRIVATE_KEY2 0x1AC
|
||||
#define FUSE_PRIVATE_KEY3 0x1B0
|
||||
#define FUSE_PRIVATE_KEY4 0x1B4
|
||||
#define FUSE_ARM_JTAG_DIS 0x1B8
|
||||
#define FUSE_BOOT_DEVICE_INFO 0x1BC
|
||||
#define FUSE_RESERVED_SW 0x1C0
|
||||
#define FUSE_OPT_VP9_DISABLE 0x1C4
|
||||
|
||||
#define FUSE_RESERVED_ODM0 0x1C8
|
||||
#define FUSE_RESERVED_ODM1 0x1CC
|
||||
#define FUSE_RESERVED_ODM2 0x1D0
|
||||
#define FUSE_RESERVED_ODM3 0x1D4
|
||||
#define FUSE_RESERVED_ODM4 0x1D8
|
||||
#define FUSE_RESERVED_ODM5 0x1DC
|
||||
#define FUSE_RESERVED_ODM6 0x1E0
|
||||
#define FUSE_RESERVED_ODM7 0x1E4
|
||||
|
||||
#define FUSE_OBS_DIS 0x1E8
|
||||
|
||||
#define FUSE_OPT_NVJTAG_PROTECTION_ENABLE_B01 0x1EC
|
||||
|
||||
#define FUSE_USB_CALIB 0x1F0
|
||||
#define FUSE_SKU_DIRECT_CONFIG 0x1F4
|
||||
#define FUSE_KFUSE_PRIVKEY_CTRL 0x1F8
|
||||
#define FUSE_PACKAGE_INFO 0x1FC // 1: MID, 2: DSC.
|
||||
#define FUSE_OPT_VENDOR_CODE 0x200
|
||||
#define FUSE_OPT_FAB_CODE 0x204
|
||||
#define FUSE_OPT_LOT_CODE_0 0x208
|
||||
#define FUSE_OPT_LOT_CODE_1 0x20C
|
||||
#define FUSE_OPT_WAFER_ID 0x210
|
||||
#define FUSE_OPT_X_COORDINATE 0x214
|
||||
#define FUSE_OPT_Y_COORDINATE 0x218
|
||||
#define FUSE_OPT_SEC_DEBUG_EN 0x21C
|
||||
#define FUSE_OPT_OPS_RESERVED 0x220
|
||||
#define FUSE_SATA_CALIB 0x224
|
||||
|
||||
#define FUSE_SPARE_REGISTER_ODM_B01 0x224
|
||||
|
||||
#define FUSE_GPU_IDDQ_CALIB 0x228
|
||||
#define FUSE_TSENSOR3_CALIB 0x22C // CPU3.
|
||||
#define FUSE_CLOCK_BONDOUT0 0x230
|
||||
#define FUSE_CLOCK_BONDOUT1 0x234
|
||||
|
||||
#define FUSE_RESERVED_ODM26_B01 0x238 // FUSE_READ_TZ Group 4.
|
||||
#define FUSE_RESERVED_ODM27_B01 0x23C // FUSE_READ_TZ Group 4.
|
||||
#define FUSE_RESERVED_ODM28_B01 0x240 // MAX77812 phase configuration. FUSE_READ_TZ Group 5.
|
||||
|
||||
#define FUSE_OPT_SAMPLE_TYPE 0x244
|
||||
#define FUSE_OPT_SUBREVISION 0x248 // "", "p", "q", "r". e.g: A01p.
|
||||
#define FUSE_OPT_SW_RESERVED_0 0x24C
|
||||
#define FUSE_OPT_SW_RESERVED_1 0x250
|
||||
#define FUSE_TSENSOR4_CALIB 0x254 // GPU.
|
||||
#define FUSE_TSENSOR5_CALIB 0x258 // MEM0.
|
||||
#define FUSE_TSENSOR6_CALIB 0x25C // MEM1.
|
||||
#define FUSE_TSENSOR7_CALIB 0x260 // PLLX.
|
||||
#define FUSE_OPT_PRIV_SEC_DIS 0x264
|
||||
#define FUSE_PKC_DISABLE 0x268
|
||||
|
||||
#define FUSE_BOOT_SECURITY_INFO_B01 0x268
|
||||
#define FUSE_OPT_RAM_RTSEL_TSMCSP_PO4HVT_B01 0x26C
|
||||
#define FUSE_OPT_RAM_WTSEL_TSMCSP_PO4HVT_B01 0x270
|
||||
#define FUSE_OPT_RAM_RTSEL_TSMCPDP_PO4HVT_B01 0x274
|
||||
#define FUSE_OPT_RAM_MTSEL_TSMCPDP_PO4HVT_B01 0x278
|
||||
|
||||
#define FUSE_FUSE2TSEC_DEBUG_DISABLE 0x27C
|
||||
#define FUSE_TSENSOR_COMMON 0x280
|
||||
#define FUSE_OPT_CP_BIN 0x284
|
||||
#define FUSE_OPT_GPU_DISABLE 0x288
|
||||
#define FUSE_OPT_FT_BIN 0x28C
|
||||
#define FUSE_OPT_DONE_MAP 0x290
|
||||
|
||||
#define FUSE_RESERVED_ODM29_B01 0x294 // FUSE_READ_TZ Group 5? Is value -1?
|
||||
|
||||
#define FUSE_APB2JTAG_DISABLE 0x298
|
||||
#define FUSE_ODM_INFO 0x29C // Debug features disable.
|
||||
#define FUSE_ARM_CRYPT_DE_FEATURE 0x2A8
|
||||
|
||||
#define FUSE_OPT_RAM_WTSEL_TSMCPDP_PO4SVT_B01 0x2B0
|
||||
#define FUSE_OPT_RAM_RCT_TSMCDP_PO4SVT_B01 0x2B4
|
||||
#define FUSE_OPT_RAM_WCT_TSMCDP_PO4SVT_B01 0x2B8
|
||||
#define FUSE_OPT_RAM_KP_TSMCDP_PO4SVT_B01 0x2BC
|
||||
|
||||
#define FUSE_WOA_SKU_FLAG 0x2C0
|
||||
#define FUSE_ECO_RESERVE_1 0x2C4
|
||||
#define FUSE_GCPLEX_CONFIG_FUSE 0x2C8
|
||||
#define FUSE_GPU_VPR_AUTO_FETCH_DIS BIT(0)
|
||||
#define FUSE_GPU_VPR_ENABLED BIT(1)
|
||||
#define FUSE_GPU_WPR_ENABLED BIT(2)
|
||||
#define FUSE_PRODUCTION_MONTH 0x2CC
|
||||
#define FUSE_RAM_REPAIR_INDICATOR 0x2D0
|
||||
#define FUSE_TSENSOR9_CALIB 0x2D4 // AOTAG.
|
||||
#define FUSE_VMIN_CALIBRATION 0x2DC
|
||||
#define FUSE_AGING_SENSOR_CALIBRATION 0x2E0
|
||||
#define FUSE_DEBUG_AUTHENTICATION 0x2E4
|
||||
#define FUSE_SECURE_PROVISION_INDEX 0x2E8
|
||||
#define FUSE_SECURE_PROVISION_INFO 0x2EC
|
||||
#define FUSE_OPT_GPU_DISABLE_CP1 0x2F0
|
||||
#define FUSE_SPARE_ENDIS 0x2F4
|
||||
#define FUSE_ECO_RESERVE_0 0x2F8 // AID.
|
||||
#define FUSE_RESERVED_CALIB0 0x304 // GPCPLL ADC Calibration.
|
||||
#define FUSE_RESERVED_CALIB1 0x308
|
||||
#define FUSE_OPT_GPU_TPC0_DISABLE 0x30C
|
||||
#define FUSE_OPT_GPU_TPC0_DISABLE_CP1 0x310
|
||||
#define FUSE_OPT_CPU_DISABLE 0x314
|
||||
#define FUSE_OPT_CPU_DISABLE_CP1 0x318
|
||||
#define FUSE_TSENSOR10_CALIB 0x31C
|
||||
#define FUSE_TSENSOR10_CALIB_AUX 0x320
|
||||
#define FUSE_OPT_RAM_SVOP_DP 0x324
|
||||
#define FUSE_OPT_RAM_SVOP_PDP 0x328
|
||||
#define FUSE_OPT_RAM_SVOP_REG 0x32C
|
||||
#define FUSE_OPT_RAM_SVOP_SP 0x330
|
||||
#define FUSE_OPT_RAM_SVOP_SMPDP 0x334
|
||||
|
||||
#define FUSE_OPT_RAM_WTSEL_TSMCPDP_PO4HVT_B01 0x324
|
||||
#define FUSE_OPT_RAM_RCT_TSMCDP_PO4HVT_B01 0x328
|
||||
#define FUSE_OPT_RAM_WCT_TSMCDP_PO4HVT_B01 0x32c
|
||||
#define FUSE_OPT_RAM_KP_TSMCDP_PO4HVT_B01 0x330
|
||||
#define FUSE_OPT_RAM_SVOP_SP_B01 0x334
|
||||
|
||||
#define FUSE_OPT_GPU_TPC0_DISABLE_CP2 0x338
|
||||
#define FUSE_OPT_GPU_TPC1_DISABLE 0x33C
|
||||
#define FUSE_OPT_GPU_TPC1_DISABLE_CP1 0x340
|
||||
#define FUSE_OPT_GPU_TPC1_DISABLE_CP2 0x344
|
||||
#define FUSE_OPT_CPU_DISABLE_CP2 0x348
|
||||
#define FUSE_OPT_GPU_DISABLE_CP2 0x34C
|
||||
#define FUSE_USB_CALIB_EXT 0x350
|
||||
#define FUSE_RESERVED_FIELD 0x354 // RMA.
|
||||
#define FUSE_SPARE_REALIGNMENT_REG 0x37C
|
||||
#define FUSE_SPARE_BIT_0 0x380
|
||||
//...
|
||||
#define FUSE_SPARE_BIT_31 0x3FC
|
||||
|
||||
/*! Fuse commands. */
|
||||
#define FUSE_IDLE 0x0
|
||||
#define FUSE_READ 0x1
|
||||
#define FUSE_WRITE 0x2
|
||||
#define FUSE_SENSE 0x3
|
||||
#define FUSE_CMD_MASK 0x3
|
||||
|
||||
/*! Fuse status. */
|
||||
#define FUSE_STATUS_RESET 0
|
||||
#define FUSE_STATUS_POST_RESET 1
|
||||
#define FUSE_STATUS_LOAD_ROW0 2
|
||||
#define FUSE_STATUS_LOAD_ROW1 3
|
||||
#define FUSE_STATUS_IDLE 4
|
||||
#define FUSE_STATUS_READ_SETUP 5
|
||||
#define FUSE_STATUS_READ_STROBE 6
|
||||
#define FUSE_STATUS_SAMPLE_FUSES 7
|
||||
#define FUSE_STATUS_READ_HOLD 8
|
||||
#define FUSE_STATUS_FUSE_SRC_SETUP 9
|
||||
#define FUSE_STATUS_WRITE_SETUP 10
|
||||
#define FUSE_STATUS_WRITE_ADDR_SETUP 11
|
||||
#define FUSE_STATUS_WRITE_PROGRAM 12
|
||||
#define FUSE_STATUS_WRITE_ADDR_HOLD 13
|
||||
#define FUSE_STATUS_FUSE_SRC_HOLD 14
|
||||
#define FUSE_STATUS_LOAD_RIR 15
|
||||
#define FUSE_STATUS_READ_BEFORE_WRITE_SETUP 16
|
||||
#define FUSE_STATUS_READ_DEASSERT_PD 17
|
||||
|
||||
/*! Fuse cache registers. */
|
||||
#define FUSE_RESERVED_ODMX(x) (0x1C8 + 4 * (x))
|
||||
|
||||
#define FUSE_ARRAY_WORDS_NUM 192
|
||||
#define FUSE_ARRAY_WORDS_NUM_B01 256
|
||||
|
||||
enum
|
||||
{
|
||||
FUSE_NX_HW_TYPE_ICOSA,
|
||||
FUSE_NX_HW_TYPE_IOWA,
|
||||
FUSE_NX_HW_TYPE_HOAG,
|
||||
FUSE_NX_HW_TYPE_AULA
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
FUSE_NX_HW_STATE_PROD,
|
||||
FUSE_NX_HW_STATE_DEV
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,756 +0,0 @@
|
||||
/**
|
||||
* @file ipc.h
|
||||
* @brief Inter-process communication handling
|
||||
* @author plutoo
|
||||
* @copyright libnx Authors (ISC License)
|
||||
*/
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
/// IPC input header magic
|
||||
#define SFCI_MAGIC 0x49434653
|
||||
/// IPC output header magic
|
||||
#define SFCO_MAGIC 0x4f434653
|
||||
|
||||
/// IPC invalid object ID
|
||||
#define IPC_INVALID_OBJECT_ID UINT32_MAX
|
||||
|
||||
///@name IPC request building
|
||||
///@{
|
||||
|
||||
/// IPC command (request) structure.
|
||||
#define IPC_MAX_BUFFERS 8
|
||||
#define IPC_MAX_OBJECTS 8
|
||||
|
||||
typedef enum {
|
||||
BufferType_Normal=0, ///< Regular buffer.
|
||||
BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory.
|
||||
BufferType_Invalid=2,
|
||||
BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping.
|
||||
} BufferType;
|
||||
|
||||
typedef enum {
|
||||
BufferDirection_Send=0,
|
||||
BufferDirection_Recv=1,
|
||||
BufferDirection_Exch=2,
|
||||
} BufferDirection;
|
||||
|
||||
typedef enum {
|
||||
IpcCommandType_Invalid = 0,
|
||||
IpcCommandType_LegacyRequest = 1,
|
||||
IpcCommandType_Close = 2,
|
||||
IpcCommandType_LegacyControl = 3,
|
||||
IpcCommandType_Request = 4,
|
||||
IpcCommandType_Control = 5,
|
||||
IpcCommandType_RequestWithContext = 6,
|
||||
IpcCommandType_ControlWithContext = 7,
|
||||
} IpcCommandType;
|
||||
|
||||
typedef enum {
|
||||
DomainMessageType_Invalid = 0,
|
||||
DomainMessageType_SendMessage = 1,
|
||||
DomainMessageType_Close = 2,
|
||||
} DomainMessageType;
|
||||
|
||||
/// IPC domain message header.
|
||||
typedef struct {
|
||||
u8 Type;
|
||||
u8 NumObjectIds;
|
||||
u16 Length;
|
||||
u32 ThisObjectId;
|
||||
u32 Pad[2];
|
||||
} DomainMessageHeader;
|
||||
|
||||
/// IPC domain response header.
|
||||
typedef struct {
|
||||
u32 NumObjectIds;
|
||||
u32 Pad[3];
|
||||
} DomainResponseHeader;
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t NumSend; // A
|
||||
size_t NumRecv; // B
|
||||
size_t NumExch; // W
|
||||
const void* Buffers[IPC_MAX_BUFFERS];
|
||||
size_t BufferSizes[IPC_MAX_BUFFERS];
|
||||
BufferType BufferTypes[IPC_MAX_BUFFERS];
|
||||
|
||||
size_t NumStaticIn; // X
|
||||
size_t NumStaticOut; // C
|
||||
const void* Statics[IPC_MAX_BUFFERS];
|
||||
size_t StaticSizes[IPC_MAX_BUFFERS];
|
||||
u8 StaticIndices[IPC_MAX_BUFFERS];
|
||||
|
||||
bool SendPid;
|
||||
size_t NumHandlesCopy;
|
||||
size_t NumHandlesMove;
|
||||
Handle Handles[IPC_MAX_OBJECTS];
|
||||
|
||||
size_t NumObjectIds;
|
||||
u32 ObjectIds[IPC_MAX_OBJECTS];
|
||||
} IpcCommand;
|
||||
|
||||
/**
|
||||
* @brief Initializes an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
*/
|
||||
static inline void ipcInitialize(IpcCommand* cmd) {
|
||||
*cmd = (IpcCommand){};
|
||||
}
|
||||
|
||||
/// IPC buffer descriptor.
|
||||
typedef struct {
|
||||
u32 Size; ///< Size of the buffer.
|
||||
u32 Addr; ///< Lower 32-bits of the address of the buffer
|
||||
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||
} IpcBufferDescriptor;
|
||||
|
||||
/// IPC static send-buffer descriptor.
|
||||
typedef struct {
|
||||
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||
u32 Addr; ///< Lower 32-bits of the address
|
||||
} IpcStaticSendDescriptor;
|
||||
|
||||
/// IPC static receive-buffer descriptor.
|
||||
typedef struct {
|
||||
u32 Addr; ///< Lower 32-bits of the address of the buffer
|
||||
u32 Packed; ///< Packed data (including higher bits of the address)
|
||||
} IpcStaticRecvDescriptor;
|
||||
|
||||
/**
|
||||
* @brief Adds a buffer to an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param buffer Address of the buffer.
|
||||
* @param size Size of the buffer.
|
||||
* @param type Buffer type.
|
||||
*/
|
||||
static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type) {
|
||||
size_t off = cmd->NumSend;
|
||||
cmd->Buffers[off] = buffer;
|
||||
cmd->BufferSizes[off] = size;
|
||||
cmd->BufferTypes[off] = type;
|
||||
cmd->NumSend++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a receive-buffer to an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param buffer Address of the buffer.
|
||||
* @param size Size of the buffer.
|
||||
* @param type Buffer type.
|
||||
*/
|
||||
static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
|
||||
size_t off = cmd->NumSend + cmd->NumRecv;
|
||||
cmd->Buffers[off] = buffer;
|
||||
cmd->BufferSizes[off] = size;
|
||||
cmd->BufferTypes[off] = type;
|
||||
cmd->NumRecv++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an exchange-buffer to an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param buffer Address of the buffer.
|
||||
* @param size Size of the buffer.
|
||||
* @param type Buffer type.
|
||||
*/
|
||||
static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) {
|
||||
size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch;
|
||||
cmd->Buffers[off] = buffer;
|
||||
cmd->BufferSizes[off] = size;
|
||||
cmd->BufferTypes[off] = type;
|
||||
cmd->NumExch++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a static-buffer to an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param buffer Address of the buffer.
|
||||
* @param size Size of the buffer.
|
||||
* @param index Index of buffer.
|
||||
*/
|
||||
static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index) {
|
||||
size_t off = cmd->NumStaticIn;
|
||||
cmd->Statics[off] = buffer;
|
||||
cmd->StaticSizes[off] = size;
|
||||
cmd->StaticIndices[off] = index;
|
||||
cmd->NumStaticIn++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a static-receive-buffer to an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param buffer Address of the buffer.
|
||||
* @param size Size of the buffer.
|
||||
* @param index Index of buffer.
|
||||
*/
|
||||
static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index) {
|
||||
size_t off = cmd->NumStaticIn + cmd->NumStaticOut;
|
||||
cmd->Statics[off] = buffer;
|
||||
cmd->StaticSizes[off] = size;
|
||||
cmd->StaticIndices[off] = index;
|
||||
cmd->NumStaticOut++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param pointer_buffer_size Pointer buffer size.
|
||||
* @param buffer Address of the buffer.
|
||||
* @param size Size of the buffer.
|
||||
* @param index Index of buffer.
|
||||
*/
|
||||
static inline void ipcAddSendSmart(IpcCommand* cmd, size_t pointer_buffer_size, const void* buffer, size_t size, u8 index) {
|
||||
if (pointer_buffer_size != 0 && size <= pointer_buffer_size) {
|
||||
ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal);
|
||||
ipcAddSendStatic(cmd, buffer, size, index);
|
||||
} else {
|
||||
ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal);
|
||||
ipcAddSendStatic(cmd, NULL, 0, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param pointer_buffer_size Pointer buffer size.
|
||||
* @param buffer Address of the buffer.
|
||||
* @param size Size of the buffer.
|
||||
* @param index Index of buffer.
|
||||
*/
|
||||
static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t pointer_buffer_size, void* buffer, size_t size, u8 index) {
|
||||
if (pointer_buffer_size != 0 && size <= pointer_buffer_size) {
|
||||
ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal);
|
||||
ipcAddRecvStatic(cmd, buffer, size, index);
|
||||
} else {
|
||||
ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal);
|
||||
ipcAddRecvStatic(cmd, NULL, 0, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Tags an IPC command structure to send the PID.
|
||||
* @param cmd IPC command structure.
|
||||
*/
|
||||
static inline void ipcSendPid(IpcCommand* cmd) {
|
||||
cmd->SendPid = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a copy-handle to be sent through an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param h Handle to send.
|
||||
* @remark The receiving process gets a copy of the handle.
|
||||
*/
|
||||
static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h) {
|
||||
cmd->Handles[cmd->NumHandlesCopy++] = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a move-handle to be sent through an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param h Handle to send.
|
||||
* @remark The sending process loses ownership of the handle, which is transferred to the receiving process.
|
||||
*/
|
||||
static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h) {
|
||||
cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares the header of an IPC command structure.
|
||||
* @param cmd IPC command structure.
|
||||
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
|
||||
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
|
||||
*/
|
||||
static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw) {
|
||||
u32* buf = (u32*)armGetTls();
|
||||
size_t i;
|
||||
*buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28);
|
||||
|
||||
u32* fill_in_size_later = buf;
|
||||
|
||||
if (cmd->NumStaticOut > 0) {
|
||||
*buf = (cmd->NumStaticOut + 2) << 10;
|
||||
}
|
||||
else {
|
||||
*buf = 0;
|
||||
}
|
||||
|
||||
if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) {
|
||||
*buf++ |= 0x80000000;
|
||||
*buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5);
|
||||
|
||||
if (cmd->SendPid)
|
||||
buf += 2;
|
||||
|
||||
for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++)
|
||||
*buf++ = cmd->Handles[i];
|
||||
}
|
||||
else {
|
||||
buf++;
|
||||
}
|
||||
|
||||
for (i=0; i<cmd->NumStaticIn; i++, buf+=2) {
|
||||
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
|
||||
|
||||
uintptr_t ptr = (uintptr_t) cmd->Statics[i];
|
||||
desc->Addr = ptr;
|
||||
desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) |
|
||||
(((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6);
|
||||
}
|
||||
|
||||
for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) {
|
||||
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
|
||||
desc->Size = cmd->BufferSizes[i];
|
||||
|
||||
uintptr_t ptr = (uintptr_t) cmd->Buffers[i];
|
||||
desc->Addr = ptr;
|
||||
desc->Packed = cmd->BufferTypes[i] |
|
||||
(((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2);
|
||||
}
|
||||
|
||||
u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4;
|
||||
u32* raw = (u32*) (buf + padding);
|
||||
|
||||
size_t raw_size = (sizeof_raw/4) + 4;
|
||||
buf += raw_size;
|
||||
|
||||
u16* buf_u16 = (u16*) buf;
|
||||
|
||||
for (i=0; i<cmd->NumStaticOut; i++) {
|
||||
size_t off = cmd->NumStaticIn + i;
|
||||
size_t sz = (uintptr_t) cmd->StaticSizes[off];
|
||||
|
||||
buf_u16[i] = (sz > 0xFFFF) ? 0 : sz;
|
||||
}
|
||||
|
||||
size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4;
|
||||
buf += u16s_size;
|
||||
raw_size += u16s_size;
|
||||
|
||||
*fill_in_size_later |= raw_size;
|
||||
|
||||
for (i=0; i<cmd->NumStaticOut; i++, buf+=2) {
|
||||
IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf;
|
||||
size_t off = cmd->NumStaticIn + i;
|
||||
|
||||
uintptr_t ptr = (uintptr_t) cmd->Statics[off];
|
||||
desc->Addr = ptr;
|
||||
desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16);
|
||||
}
|
||||
|
||||
return (void*) raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dispatches an IPC request.
|
||||
* @param session IPC session handle.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcDispatch(Handle session) {
|
||||
return svcSendSyncRequest(session);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
///@name IPC response parsing
|
||||
///@{
|
||||
|
||||
/// IPC parsed command (response) structure.
|
||||
typedef struct {
|
||||
IpcCommandType CommandType; ///< Type of the command
|
||||
|
||||
bool HasPid; ///< true if the 'Pid' field is filled out.
|
||||
u64 Pid; ///< PID included in the response (only if HasPid is true)
|
||||
|
||||
size_t NumHandles; ///< Number of handles copied.
|
||||
Handle Handles[IPC_MAX_OBJECTS]; ///< Handles.
|
||||
bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied.
|
||||
|
||||
bool IsDomainRequest; ///< true if the the message is a Domain message.
|
||||
DomainMessageType InMessageType; ///< Type of the domain message.
|
||||
u32 InMessageLength; ///< Size of rawdata (for domain messages).
|
||||
u32 InThisObjectId; ///< Object ID to call the command on (for domain messages).
|
||||
size_t InNumObjectIds; ///< Number of object IDs (for domain messages).
|
||||
u32 InObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages).
|
||||
|
||||
bool IsDomainResponse; ///< true if the the message is a Domain response.
|
||||
size_t OutNumObjectIds; ///< Number of object IDs (for domain responses).
|
||||
u32 OutObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain responses).
|
||||
|
||||
size_t NumBuffers; ///< Number of buffers in the response.
|
||||
void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers.
|
||||
size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers.
|
||||
BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers.
|
||||
BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer.
|
||||
|
||||
size_t NumStatics; ///< Number of statics in the response.
|
||||
void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics.
|
||||
size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics.
|
||||
u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics.
|
||||
|
||||
size_t NumStaticsOut; ///< Number of output statics available in the response.
|
||||
|
||||
void* Raw; ///< Pointer to the raw embedded data structure in the response.
|
||||
void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding.
|
||||
size_t RawSize; ///< Size of the raw embedded data.
|
||||
} IpcParsedCommand;
|
||||
|
||||
/**
|
||||
* @brief Parse an IPC command response into an IPC parsed command structure.
|
||||
* @param r IPC parsed command structure to fill in.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcParse(IpcParsedCommand* r) {
|
||||
u32* buf = (u32*)armGetTls();
|
||||
u32 ctrl0 = *buf++;
|
||||
u32 ctrl1 = *buf++;
|
||||
size_t i;
|
||||
|
||||
r->IsDomainRequest = false;
|
||||
r->IsDomainResponse = false;
|
||||
|
||||
r->CommandType = (IpcCommandType) (ctrl0 & 0xffff);
|
||||
r->HasPid = false;
|
||||
r->RawSize = (ctrl1 & 0x1ff) * 4;
|
||||
r->NumHandles = 0;
|
||||
|
||||
r->NumStaticsOut = (ctrl1 >> 10) & 15;
|
||||
if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor
|
||||
if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors
|
||||
|
||||
if (ctrl1 & 0x80000000) {
|
||||
u32 ctrl2 = *buf++;
|
||||
|
||||
if (ctrl2 & 1) {
|
||||
r->HasPid = true;
|
||||
r->Pid = *buf++;
|
||||
r->Pid |= ((u64)(*buf++)) << 32;
|
||||
}
|
||||
|
||||
size_t num_handles_copy = ((ctrl2 >> 1) & 15);
|
||||
size_t num_handles_move = ((ctrl2 >> 5) & 15);
|
||||
|
||||
size_t num_handles = num_handles_copy + num_handles_move;
|
||||
u32* buf_after_handles = buf + num_handles;
|
||||
|
||||
if (num_handles > IPC_MAX_OBJECTS)
|
||||
num_handles = IPC_MAX_OBJECTS;
|
||||
|
||||
for (i=0; i<num_handles; i++)
|
||||
{
|
||||
r->Handles[i] = *(buf+i);
|
||||
r->WasHandleCopied[i] = (i < num_handles_copy);
|
||||
}
|
||||
|
||||
r->NumHandles = num_handles;
|
||||
buf = buf_after_handles;
|
||||
}
|
||||
|
||||
size_t num_statics = (ctrl0 >> 16) & 15;
|
||||
u32* buf_after_statics = buf + num_statics*2;
|
||||
|
||||
if (num_statics > IPC_MAX_BUFFERS)
|
||||
num_statics = IPC_MAX_BUFFERS;
|
||||
|
||||
for (i=0; i<num_statics; i++, buf+=2) {
|
||||
IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf;
|
||||
u64 packed = (u64) desc->Packed;
|
||||
|
||||
r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36));
|
||||
r->StaticSizes[i] = packed >> 16;
|
||||
r->StaticIndices[i] = packed & 63;
|
||||
}
|
||||
|
||||
r->NumStatics = num_statics;
|
||||
buf = buf_after_statics;
|
||||
|
||||
size_t num_bufs_send = (ctrl0 >> 20) & 15;
|
||||
size_t num_bufs_recv = (ctrl0 >> 24) & 15;
|
||||
size_t num_bufs_exch = (ctrl0 >> 28) & 15;
|
||||
|
||||
size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch;
|
||||
r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15);
|
||||
r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3));
|
||||
|
||||
if (num_bufs > IPC_MAX_BUFFERS)
|
||||
num_bufs = IPC_MAX_BUFFERS;
|
||||
|
||||
for (i=0; i<num_bufs; i++, buf+=3) {
|
||||
IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf;
|
||||
u64 packed = (u64) desc->Packed;
|
||||
|
||||
r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36));
|
||||
r->BufferSizes[i] = desc->Size;
|
||||
r->BufferTypes[i] = (BufferType) (packed & 3);
|
||||
|
||||
if (i < num_bufs_send)
|
||||
r->BufferDirections[i] = BufferDirection_Send;
|
||||
else if (i < (num_bufs_send + num_bufs_recv))
|
||||
r->BufferDirections[i] = BufferDirection_Recv;
|
||||
else
|
||||
r->BufferDirections[i] = BufferDirection_Exch;
|
||||
}
|
||||
|
||||
r->NumBuffers = num_bufs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Queries the size of an IPC pointer buffer.
|
||||
* @param session IPC session handle.
|
||||
* @param size Output variable in which to store the size.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) {
|
||||
u32* buf = (u32*)armGetTls();
|
||||
|
||||
buf[0] = IpcCommandType_Control;
|
||||
buf[1] = 8;
|
||||
buf[2] = 0;
|
||||
buf[3] = 0;
|
||||
buf[4] = SFCI_MAGIC;
|
||||
buf[5] = 0;
|
||||
buf[6] = 3;
|
||||
buf[7] = 0;
|
||||
|
||||
Result rc = ipcDispatch(session);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct ipcQueryPointerBufferSizeResponse {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u32 size;
|
||||
} *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw;
|
||||
|
||||
rc = raw->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*size = raw->size & 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Closes the IPC session with proper clean up.
|
||||
* @param session IPC session handle.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcCloseSession(Handle session) {
|
||||
u32* buf = (u32*)armGetTls();
|
||||
buf[0] = IpcCommandType_Close;
|
||||
buf[1] = 0;
|
||||
return ipcDispatch(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clones an IPC session.
|
||||
* @param session IPC session handle.
|
||||
* @param unk Unknown.
|
||||
* @param new_session_out Output cloned IPC session handle.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcCloneSession(Handle session, u32 unk, Handle* new_session_out) {
|
||||
u32* buf = (u32*)armGetTls();
|
||||
|
||||
buf[0] = IpcCommandType_Control;
|
||||
buf[1] = 9;
|
||||
buf[2] = 0;
|
||||
buf[3] = 0;
|
||||
buf[4] = SFCI_MAGIC;
|
||||
buf[5] = 0;
|
||||
buf[6] = 4;
|
||||
buf[7] = 0;
|
||||
buf[8] = unk;
|
||||
|
||||
Result rc = ipcDispatch(session);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct ipcCloneSessionResponse {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
} *raw = (struct ipcCloneSessionResponse*)r.Raw;
|
||||
|
||||
rc = raw->result;
|
||||
|
||||
if (R_SUCCEEDED(rc) && new_session_out) {
|
||||
*new_session_out = r.Handles[0];
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
///@name IPC domain handling
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Converts an IPC session handle into a domain.
|
||||
* @param session IPC session handle.
|
||||
* @param object_id_out Output variable in which to store the object ID.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) {
|
||||
u32* buf = (u32*)armGetTls();
|
||||
|
||||
buf[0] = IpcCommandType_Control;
|
||||
buf[1] = 8;
|
||||
buf[4] = SFCI_MAGIC;
|
||||
buf[5] = 0;
|
||||
buf[6] = 0;
|
||||
buf[7] = 0;
|
||||
|
||||
Result rc = ipcDispatch(session);
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
IpcParsedCommand r;
|
||||
ipcParse(&r);
|
||||
|
||||
struct ipcConvertSessionToDomainResponse {
|
||||
u64 magic;
|
||||
u64 result;
|
||||
u32 object_id;
|
||||
} *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw;
|
||||
|
||||
rc = raw->result;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
*object_id_out = raw->object_id;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an object ID to be sent through an IPC domain command structure.
|
||||
* @param cmd IPC domain command structure.
|
||||
* @param object_id Object ID to send.
|
||||
*/
|
||||
static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id) {
|
||||
cmd->ObjectIds[cmd->NumObjectIds++] = object_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepares the header of an IPC command structure (domain version).
|
||||
* @param cmd IPC command structure.
|
||||
* @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request
|
||||
* @param object_id Domain object ID.
|
||||
* @return Pointer to the raw embedded data structure in the request, ready to be filled out.
|
||||
*/
|
||||
static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id) {
|
||||
void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader) + cmd->NumObjectIds*sizeof(u32));
|
||||
DomainMessageHeader* hdr = (DomainMessageHeader*) raw;
|
||||
u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw);
|
||||
|
||||
hdr->Type = DomainMessageType_SendMessage;
|
||||
hdr->NumObjectIds = (u8)cmd->NumObjectIds;
|
||||
hdr->Length = sizeof_raw;
|
||||
hdr->ThisObjectId = object_id;
|
||||
hdr->Pad[0] = hdr->Pad[1] = 0;
|
||||
|
||||
for(size_t i = 0; i < cmd->NumObjectIds; i++)
|
||||
object_ids[i] = cmd->ObjectIds[i];
|
||||
return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse an IPC command request into an IPC parsed command structure (domain version).
|
||||
* @param r IPC parsed command structure to fill in.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcParseDomainRequest(IpcParsedCommand* r) {
|
||||
Result rc = ipcParse(r);
|
||||
DomainMessageHeader *hdr;
|
||||
u32 *object_ids;
|
||||
if(R_FAILED(rc))
|
||||
return rc;
|
||||
|
||||
hdr = (DomainMessageHeader*) r->Raw;
|
||||
object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length);
|
||||
r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader));
|
||||
|
||||
r->IsDomainRequest = true;
|
||||
r->InMessageType = (DomainMessageType)(hdr->Type);
|
||||
switch (r->InMessageType) {
|
||||
case DomainMessageType_SendMessage:
|
||||
case DomainMessageType_Close:
|
||||
break;
|
||||
default:
|
||||
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType);
|
||||
}
|
||||
|
||||
r->InThisObjectId = hdr->ThisObjectId;
|
||||
r->InNumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
|
||||
if ((uintptr_t)object_ids + sizeof(u32) * r->InNumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
|
||||
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
|
||||
}
|
||||
for(size_t i = 0; i < r->InNumObjectIds; i++)
|
||||
r->InObjectIds[i] = object_ids[i];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse an IPC command response into an IPC parsed command structure (domain version).
|
||||
* @param r IPC parsed command structure to fill in.
|
||||
* @param sizeof_raw Size in bytes of the raw data structure.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcParseDomainResponse(IpcParsedCommand* r, size_t sizeof_raw) {
|
||||
Result rc = ipcParse(r);
|
||||
DomainResponseHeader *hdr;
|
||||
u32 *object_ids;
|
||||
if(R_FAILED(rc))
|
||||
return rc;
|
||||
|
||||
hdr = (DomainResponseHeader*) r->Raw;
|
||||
r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainResponseHeader));
|
||||
object_ids = (u32*)(((uintptr_t) r->Raw) + sizeof_raw);//Official sw doesn't align this.
|
||||
|
||||
r->IsDomainResponse = true;
|
||||
|
||||
r->OutNumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds;
|
||||
if ((uintptr_t)object_ids + sizeof(u32) * r->OutNumObjectIds - (uintptr_t)armGetTls() >= 0x100) {
|
||||
return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds);
|
||||
}
|
||||
for(size_t i = 0; i < r->OutNumObjectIds; i++)
|
||||
r->OutObjectIds[i] = object_ids[i];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Closes a domain object by ID.
|
||||
* @param session IPC session handle.
|
||||
* @param object_id ID of the object to close.
|
||||
* @return Result code.
|
||||
*/
|
||||
static inline Result ipcCloseObjectById(Handle session, u32 object_id) {
|
||||
IpcCommand c;
|
||||
DomainMessageHeader* hdr;
|
||||
|
||||
ipcInitialize(&c);
|
||||
hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader));
|
||||
|
||||
hdr->Type = DomainMessageType_Close;
|
||||
hdr->NumObjectIds = 0;
|
||||
hdr->Length = 0;
|
||||
hdr->ThisObjectId = object_id;
|
||||
hdr->Pad[0] = hdr->Pad[1] = 0;
|
||||
|
||||
return ipcDispatch(session); // this command has no associated response
|
||||
}
|
||||
|
||||
///@}
|
||||
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
#include <switch.h>
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#include <hocclk/client/ipc.h>
|
||||
|
||||
static Service g_hocclkSrv;
|
||||
static atomic_size_t g_refCnt;
|
||||
|
||||
bool hocclkIpcRunning()
|
||||
{
|
||||
Handle handle;
|
||||
bool running = R_FAILED(smRegisterService(&handle, smEncodeName(HOCCLK_IPC_SERVICE_NAME), false, 1));
|
||||
|
||||
if (!running)
|
||||
{
|
||||
smUnregisterService(smEncodeName(HOCCLK_IPC_SERVICE_NAME));
|
||||
}
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
Result hocclkIpcInitialize(void)
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
g_refCnt++;
|
||||
|
||||
if (serviceIsActive(&g_hocclkSrv))
|
||||
return 0;
|
||||
|
||||
rc = smGetService(&g_hocclkSrv, HOCCLK_IPC_SERVICE_NAME);
|
||||
|
||||
if (R_FAILED(rc)) hocclkIpcExit();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void hocclkIpcExit(void)
|
||||
{
|
||||
if (--g_refCnt == 0)
|
||||
{
|
||||
serviceClose(&g_hocclkSrv);
|
||||
}
|
||||
}
|
||||
|
||||
Result hocclkIpcGetAPIVersion(u32* out_ver)
|
||||
{
|
||||
return serviceDispatchOut(&g_hocclkSrv, HocClkIpcCmd_GetApiVersion, *out_ver);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetVersionString(char* out, size_t len)
|
||||
{
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetVersionString,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out, len}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetCurrentContext(HocClkContext* out_context)
|
||||
{
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetCurrentContext,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_context, sizeof(HocClkContext)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetProfileCount(u64 tid, u8* out_count)
|
||||
{
|
||||
return serviceDispatchInOut(&g_hocclkSrv, HocClkIpcCmd_GetProfileCount, tid, *out_count);
|
||||
}
|
||||
|
||||
Result hocclkIpcSetEnabled(bool enabled)
|
||||
{
|
||||
u8 enabledRaw = (u8)enabled;
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetEnabled, enabledRaw);
|
||||
}
|
||||
|
||||
Result hocclkIpcSetOverride(HocClkModule module, u32 hz)
|
||||
{
|
||||
HocClkIpc_SetOverride_Args args = {
|
||||
.module = module,
|
||||
.hz = hz
|
||||
};
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetOverride, args);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetProfiles(u64 tid, HocClkTitleProfileList* out_profiles)
|
||||
{
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetProfiles, tid,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_profiles, sizeof(HocClkTitleProfileList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcSetProfiles(u64 tid, HocClkTitleProfileList* profiles)
|
||||
{
|
||||
HocClkIpc_SetProfiles_Args args;
|
||||
args.tid = tid;
|
||||
memcpy(&args.profiles, profiles, sizeof(HocClkTitleProfileList));
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetProfiles, args);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetConfigValues(HocClkConfigValueList* out_configValues)
|
||||
{
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_configValues, sizeof(HocClkConfigValueList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcSetConfigValues(HocClkConfigValueList* configValues)
|
||||
{
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_SetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
|
||||
.buffers = {{configValues, sizeof(HocClkConfigValueList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocclkIpcGetFreqList(HocClkModule module, u32* list, u32 maxCount, u32* outCount)
|
||||
{
|
||||
HocClkIpc_GetFreqList_Args args = {
|
||||
.module = module,
|
||||
.maxCount = maxCount
|
||||
};
|
||||
return serviceDispatchInOut(&g_hocclkSrv, HocClkIpcCmd_GetFreqList, args, *outCount,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{list, maxCount * sizeof(u32)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result hocClkIpcSetKipData()
|
||||
{
|
||||
u32 temp = 0;
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetKipData, temp);
|
||||
}
|
||||
|
||||
Result hocClkIpcGetKipData()
|
||||
{
|
||||
u32 temp = 0;
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetKipData, temp);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crc32.h>
|
||||
|
||||
namespace crc32 {
|
||||
uint32_t crc32(const uint8_t *data, size_t length) {
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
crc ^= data[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
uint32_t checksum_file(const char *filename) {
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
perror("[crc32] Error opening file");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t buffer[1024];
|
||||
uint32_t crc = 0xFFFFFFFF;
|
||||
size_t bytes_read;
|
||||
|
||||
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
||||
for (size_t i = 0; i < bytes_read; i++) {
|
||||
crc ^= buffer[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return ~crc;
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Informationen",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Modul:",
|
||||
"sys-dock status:": "Sys-Dock-Status:",
|
||||
"SaltyNX status:": "SaltyNX-Status:",
|
||||
"RR Display status:": "RR Anzeigestatus:",
|
||||
"Wafer Position:": "Waferposition:",
|
||||
"Credits": "Credits",
|
||||
"Developers": "Entwickler",
|
||||
"Contributors": "Mitwirkende",
|
||||
"Testers": "Tester",
|
||||
"Special Thanks": "Besonderer Dank",
|
||||
"Unknown": "Unbekannt",
|
||||
"Installed": "Installiert",
|
||||
"Not Installed": "Nicht installiert",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "DIE BIERWAREN-LIZENZ",
|
||||
"Default": "Standard",
|
||||
"Do Not Override": "Nicht überschreiben",
|
||||
"Disabled": "Deaktiviert",
|
||||
"Enabled": "Aktiviert",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Zurücksetzen",
|
||||
"Display": "Anzeige",
|
||||
"Application changed\\n\\n": "Anwendung geändert\\n\\n",
|
||||
"The running application changed\\n\\n": "Die laufende Anwendung hat sich geändert\\n\\n",
|
||||
"while editing was going on.": "während die Bearbeitung im Gange war.",
|
||||
"Board": "Vorstand",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Es konnte keine Verbindung zum hoc-clk-Systemmodul hergestellt werden.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Bitte stellen Sie sicher, dass alles in Ordnung ist\\n\\n",
|
||||
"correctly installed and enabled.": "korrekt installiert und aktiviert.",
|
||||
"Fatal error": "Fataler Fehler",
|
||||
"Temporary Overrides ": "Temporäre Überschreibungen",
|
||||
"Sleep Mode": "Schlafmodus",
|
||||
"Stock": "Lager",
|
||||
"Dev OC": "Entwickler OC",
|
||||
"Boost Mode": "Boost-Modus",
|
||||
"Safe Max": "Sicher max",
|
||||
"Unsafe Max": "Unsicher max",
|
||||
"Absolute Max": "Absolutes Maximum",
|
||||
"Handheld Safe Max": "Handsafe max",
|
||||
"Enable": "Aktivieren",
|
||||
"Edit App Profile": "App-Profil bearbeiten",
|
||||
"Edit Global Profile": "Globales Profil bearbeiten",
|
||||
"Temporary Overrides": "Temporäre Überschreibungen",
|
||||
"Settings": "Einstellungen",
|
||||
"About": "Über",
|
||||
"Compiling with minimal features": "Kompilieren mit minimalen Funktionen",
|
||||
"General Settings": "Allgemeine Einstellungen",
|
||||
"Governor Settings": "Gouverneurseinstellungen",
|
||||
"Safety Settings": "Sicherheitseinstellungen",
|
||||
"Save KIP Settings": "Speichern Sie die KIP-Einstellungen",
|
||||
"RAM Settings": "RAM-Einstellungen",
|
||||
"CPU Settings": "CPU-Einstellungen",
|
||||
"GPU Settings": "GPU-Einstellungen",
|
||||
"Display Settings": "Anzeigeeinstellungen",
|
||||
"Experimental": "Experimentell",
|
||||
"GPU Scheduling Override Method": "GPU-Planungsüberschreibungsmethode",
|
||||
"can be dangerous and may cause": "kann gefährlich sein und verursachen",
|
||||
"damage to your battery or charger!": "Schäden an Ihrem Akku oder Ladegerät!",
|
||||
"Charge Current Override": "Ladestrom-Überbrückung",
|
||||
"RAM Voltage Display Mode": "RAM-Spannungsanzeigemodus",
|
||||
"Polling Interval": "Abfrageintervall",
|
||||
"CPU Governor Minimum Frequency": "Mindestfrequenz des CPU-Reglers",
|
||||
"refresh rates may cause stress": "Bildwiederholraten können Stress verursachen",
|
||||
"or damage to your display! ": "oder Schäden an Ihrem Display!",
|
||||
"Proceed at your own risk!": "Das Vorgehen erfolgt auf eigene Gefahr!",
|
||||
"Max Handheld Display": "Max Handheld-Display",
|
||||
"Display Clock": "Uhr anzeigen",
|
||||
"Official Rating": "Offizielle Bewertung",
|
||||
"TDP Threshold": "TDP-Schwellenwert",
|
||||
"Power": "Macht",
|
||||
"Thermal Throttle Limit": "Thermische Drosselgrenze",
|
||||
"HP Mode": "HP-Modus",
|
||||
"Default (Mariko)": "Standard (Mariko)",
|
||||
"Default (Erista)": "Standard (Erista)",
|
||||
"Rating": "Bewertung",
|
||||
"Safe Max (Mariko)": "Safe Max (Mariko)",
|
||||
"Safe Max (Erista)": "Safe Max (Erista)",
|
||||
"RAM VDD2 Voltage": "RAM VDD2 Spannung",
|
||||
"Voltage": "Spannung",
|
||||
"RAM VDDQ Voltage": "RAM-VDDQ-Spannung",
|
||||
"RAM Frequency Editor": "RAM-Frequenzeditor",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Hoher Tacho erforderlich!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (Benötigt extremen Tacho/PLL)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (Benötigt extremen Tacho/PLL)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (Benötigt extremen Tacho/PLL)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 MHz (Benötigt lächerlichen Tacho/PLL)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (Benötigt lächerlichen Tacho/PLL)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (Benötigt lächerlichen Tacho/PLL)",
|
||||
"Ram Max Clock": "Ram Max Uhr",
|
||||
"RAM Latency Editor": "RAM-Latenz-Editor",
|
||||
"RAM Timing Reductions": "Reduzierung des RAM-Timings",
|
||||
"Memory Timings": "Speicherzeiten",
|
||||
"Advanced": "Fortgeschritten",
|
||||
"t6 tRTW Fine Tune": "t6 tRTW Feinabstimmung",
|
||||
"tRTW Fine Tune": "tRTW-Feinabstimmung",
|
||||
"t7 tWTR Fine Tune": "t7 tWTR Feinabstimmung",
|
||||
"tWTR Fine Tune": "tWTR-Feinabstimmung",
|
||||
"Memory Latencies": "Speicherlatenzen",
|
||||
"Read Latency": "Leselatenz",
|
||||
"Write Latency": "Schreiblatenz",
|
||||
"CPU Boost Clock": "CPU-Boost-Takt",
|
||||
"CPU UV": "CPU-UV",
|
||||
"CPU Unlock": "CPU-Entsperrung",
|
||||
"CPU VMIN": "CPU-VMIN",
|
||||
"CPU Max Voltage": "Maximale CPU-Spannung",
|
||||
"CPU Max Clock": "Maximaler CPU-Takt",
|
||||
"Extreme UV Table": "Extremer UV-Tisch",
|
||||
"CPU UV Table": "CPU-UV-Tisch",
|
||||
"CPU Low UV": "CPU-niedrige UV-Strahlung",
|
||||
"CPU High UV": "CPU Hohe UV-Strahlung",
|
||||
"CPU Low VMIN": "CPU niedrig VMIN",
|
||||
"CPU High VMIN": "CPU hoch VMIN",
|
||||
"No Undervolt": "Kein Undervolt",
|
||||
"SLT Table": "SLT-Tisch",
|
||||
"HiOPT Table": "HiOPT-Tabelle",
|
||||
"GPU Undervolt Table": "GPU-Unterspannungstabelle",
|
||||
"GPU Minimum Voltage": "GPU-Mindestspannung",
|
||||
"Calculate GPU Vmin": "Berechnen Sie die GPU-Vmin",
|
||||
"GPU VMIN": "GPU-VMIN",
|
||||
"GPU Maximum Voltage": "Maximale GPU-Spannung",
|
||||
"GPU Voltage Offset": "GPU-Spannungsoffset",
|
||||
"Do not override": "Nicht überschreiben",
|
||||
"Enabled (Default)": "Aktiviert (Standard)",
|
||||
"96.6% limit": "96,6 %-Grenze",
|
||||
"99.7% limit": "99,7 %-Grenze",
|
||||
"GPU Scheduling Override": "GPU-Planungsüberschreibung",
|
||||
"Official Service": "Offizieller Dienst",
|
||||
"GPU DVFS Mode": "GPU-DVFS-Modus",
|
||||
"GPU DVFS Offset": "GPU-DVFS-Offset",
|
||||
"GPU Voltage Table": "GPU-Spannungstabelle",
|
||||
"GPU Custom Table (mV)": "Benutzerdefinierte GPU-Tabelle (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075 MHz ohne UV, 1152 MHz auf SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "oder 1228 MHz auf HiOPT kann dazu führen",
|
||||
"permanent damage to your Switch!": "Dauerhafter Schaden an Ihrem Switch!",
|
||||
"921MHz without UV and 960MHz on": "921 MHz ohne UV und 960 MHz eingeschaltet",
|
||||
"SLT or HiOPT can cause ": "SLT oder HiOPT können dazu führen"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Information",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Module: ",
|
||||
"sys-dock status:": "sys-dock status:",
|
||||
"SaltyNX status:": "SaltyNX status:",
|
||||
"RR Display status:": "RR Display status:",
|
||||
"Wafer Position:": "Wafer Position:",
|
||||
"Credits": "Credits",
|
||||
"Developers": "Developers",
|
||||
"Contributors": "Contributors",
|
||||
"Testers": "Testers",
|
||||
"Special Thanks": "Special Thanks",
|
||||
"Unknown": "Unknown",
|
||||
"Installed": "Installed",
|
||||
"Not Installed": "Not Installed",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "THE BEER-WARE LICENSE",
|
||||
"Default": "Default",
|
||||
"Do Not Override": "Do Not Override",
|
||||
"Disabled": "Disabled",
|
||||
"Enabled": "Enabled",
|
||||
" \\ue0e3 Reset": " \\ue0e3 Reset",
|
||||
"Display": "Display",
|
||||
"Application changed\\n\\n": "Application changed\\n\\n",
|
||||
"The running application changed\\n\\n": "The running application changed\\n\\n",
|
||||
"while editing was going on.": "while editing was going on.",
|
||||
"Board": "Board",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Could not connect to hoc-clk sysmodule.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Please make sure everything is\\n\\n",
|
||||
"correctly installed and enabled.": "correctly installed and enabled.",
|
||||
"Fatal error": "Fatal error",
|
||||
"Temporary Overrides ": "Temporary Overrides ",
|
||||
"Sleep Mode": "Sleep Mode",
|
||||
"Stock": "Stock",
|
||||
"Dev OC": "Dev OC",
|
||||
"Boost Mode": "Boost Mode",
|
||||
"Safe Max": "Safe Max",
|
||||
"Unsafe Max": "Unsafe Max",
|
||||
"Absolute Max": "Absolute Max",
|
||||
"Handheld Safe Max": "Handheld Safe Max",
|
||||
"Enable": "Enable",
|
||||
"Edit App Profile": "Edit App Profile",
|
||||
"Edit Global Profile": "Edit Global Profile",
|
||||
"Temporary Overrides": "Temporary Overrides",
|
||||
"Settings": "Settings",
|
||||
"About": "About",
|
||||
"Compiling with minimal features": "Compiling with minimal features",
|
||||
"General Settings": "General Settings",
|
||||
"Governor Settings": "Governor Settings",
|
||||
"Safety Settings": "Safety Settings",
|
||||
"Save KIP Settings": "Save KIP Settings",
|
||||
"RAM Settings": "RAM Settings",
|
||||
"CPU Settings": "CPU Settings",
|
||||
"GPU Settings": "GPU Settings",
|
||||
"Display Settings": "Display Settings",
|
||||
"Experimental": "Experimental",
|
||||
"GPU Scheduling Override Method": "GPU Scheduling Override Method",
|
||||
"can be dangerous and may cause": "can be dangerous and may cause",
|
||||
"damage to your battery or charger!": "damage to your battery or charger!",
|
||||
"Charge Current Override": "Charge Current Override",
|
||||
"RAM Voltage Display Mode": "RAM Voltage Display Mode",
|
||||
"Polling Interval": "Polling Interval",
|
||||
"CPU Governor Minimum Frequency": "CPU Governor Minimum Frequency",
|
||||
"refresh rates may cause stress": "refresh rates may cause stress",
|
||||
"or damage to your display! ": "or damage to your display! ",
|
||||
"Proceed at your own risk!": "Proceed at your own risk!",
|
||||
"Max Handheld Display": "Max Handheld Display",
|
||||
"Display Clock": "Display Clock",
|
||||
"Official Rating": "Official Rating",
|
||||
"TDP Threshold": "TDP Threshold",
|
||||
"Power": "Power",
|
||||
"Thermal Throttle Limit": "Thermal Throttle Limit",
|
||||
"HP Mode": "HP Mode",
|
||||
"Default (Mariko)": "Default (Mariko)",
|
||||
"Default (Erista)": "Default (Erista)",
|
||||
"Rating": "Rating",
|
||||
"Safe Max (Mariko)": "Safe Max (Mariko)",
|
||||
"Safe Max (Erista)": "Safe Max (Erista)",
|
||||
"RAM VDD2 Voltage": "RAM VDD2 Voltage",
|
||||
"Voltage": "Voltage",
|
||||
"RAM VDDQ Voltage": "RAM VDDQ Voltage",
|
||||
"RAM Frequency Editor": "RAM Frequency Editor",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "High speedo needed!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333MHz (Needs extreme Speedo/PLL)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366MHz (Needs extreme Speedo/PLL)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400MHz (Needs extreme Speedo/PLL)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (Needs ridiculous Speedo/PLL)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (Needs ridiculous Speedo/PLL)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz (Needs ridiculous Speedo/PLL)",
|
||||
"Ram Max Clock": "Ram Max Clock",
|
||||
"RAM Latency Editor": "RAM Latency Editor",
|
||||
"RAM Timing Reductions": "RAM Timing Reductions",
|
||||
"Memory Timings": "Memory Timings",
|
||||
"Advanced": "Advanced",
|
||||
"t6 tRTW Fine Tune": "t6 tRTW Fine Tune",
|
||||
"tRTW Fine Tune": "tRTW Fine Tune",
|
||||
"t7 tWTR Fine Tune": "t7 tWTR Fine Tune",
|
||||
"tWTR Fine Tune": "tWTR Fine Tune",
|
||||
"Memory Latencies": "Memory Latencies",
|
||||
"Read Latency": "Read Latency",
|
||||
"Write Latency": "Write Latency",
|
||||
"CPU Boost Clock": "CPU Boost Clock",
|
||||
"CPU UV": "CPU UV",
|
||||
"CPU Unlock": "CPU Unlock",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "CPU Max Voltage",
|
||||
"CPU Max Clock": "CPU Max Clock",
|
||||
"Extreme UV Table": "Extreme UV Table",
|
||||
"CPU UV Table": "CPU UV Table",
|
||||
"CPU Low UV": "CPU Low UV",
|
||||
"CPU High UV": "CPU High UV",
|
||||
"CPU Low VMIN": "CPU Low VMIN",
|
||||
"CPU High VMIN": "CPU High VMIN",
|
||||
"No Undervolt": "No Undervolt",
|
||||
"SLT Table": "SLT Table",
|
||||
"HiOPT Table": "HiOPT Table",
|
||||
"GPU Undervolt Table": "GPU Undervolt Table",
|
||||
"GPU Minimum Voltage": "GPU Minimum Voltage",
|
||||
"Calculate GPU Vmin": "Calculate GPU Vmin",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "GPU Maximum Voltage",
|
||||
"GPU Voltage Offset": "GPU Voltage Offset",
|
||||
"Do not override": "Do not override",
|
||||
"Enabled (Default)": "Enabled (Default)",
|
||||
"96.6% limit": "96.6% limit",
|
||||
"99.7% limit": "99.7% limit",
|
||||
"GPU Scheduling Override": "GPU Scheduling Override",
|
||||
"Official Service": "Official Service",
|
||||
"GPU DVFS Mode": "GPU DVFS Mode",
|
||||
"GPU DVFS Offset": "GPU DVFS Offset",
|
||||
"GPU Voltage Table": "GPU Voltage Table",
|
||||
"GPU Custom Table (mV)": "GPU Custom Table (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075MHz without UV, 1152MHz on SLT",
|
||||
"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 "
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Información",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Módulo:",
|
||||
"sys-dock status:": "estado del sys-dock:",
|
||||
"SaltyNX status:": "Estado de SaltyNX:",
|
||||
"RR Display status:": "Estado de visualización RR:",
|
||||
"Wafer Position:": "Posición de la oblea:",
|
||||
"Credits": "Créditos",
|
||||
"Developers": "Desarrolladores",
|
||||
"Contributors": "Colaboradores",
|
||||
"Testers": "Probadores",
|
||||
"Special Thanks": "agradecimiento especial",
|
||||
"Unknown": "Desconocido",
|
||||
"Installed": "Instalado",
|
||||
"Not Installed": "No instalado",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "LA LICENCIA DE CERVEZA",
|
||||
"Default": "Predeterminado",
|
||||
"Do Not Override": "No anular",
|
||||
"Disabled": "Discapacitado",
|
||||
"Enabled": "Habilitado",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Restablecer",
|
||||
"Display": "Pantalla",
|
||||
"Application changed\\n\\n": "Aplicación modificada\\n\\n",
|
||||
"The running application changed\\n\\n": "La aplicación en ejecución cambió\\n\\n",
|
||||
"while editing was going on.": "mientras se realizaba la edición.",
|
||||
"Board": "tablero",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "No se pudo conectar al módulo del sistema hoc-clk.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Por favor asegúrese de que todo esté\\n\\n",
|
||||
"correctly installed and enabled.": "correctamente instalado y habilitado.",
|
||||
"Fatal error": "error fatal",
|
||||
"Temporary Overrides ": "Anulaciones temporales",
|
||||
"Sleep Mode": "Modo de suspensión",
|
||||
"Stock": "Valores",
|
||||
"Dev OC": "Desarrollador OC",
|
||||
"Boost Mode": "Modo de impulso",
|
||||
"Safe Max": "Máximo seguro",
|
||||
"Unsafe Max": "Máximo inseguro",
|
||||
"Absolute Max": "Máximo absoluto",
|
||||
"Handheld Safe Max": "Caja fuerte de mano máx.",
|
||||
"Enable": "Habilitar",
|
||||
"Edit App Profile": "Editar perfil de aplicación",
|
||||
"Edit Global Profile": "Editar perfil global",
|
||||
"Temporary Overrides": "Anulaciones temporales",
|
||||
"Settings": "Configuración",
|
||||
"About": "Acerca de",
|
||||
"Compiling with minimal features": "Compilando con características mínimas",
|
||||
"General Settings": "Configuraciones generales",
|
||||
"Governor Settings": "Configuración del gobernador",
|
||||
"Safety Settings": "Configuraciones de seguridad",
|
||||
"Save KIP Settings": "Guardar configuración de KIP",
|
||||
"RAM Settings": "Configuración de RAM",
|
||||
"CPU Settings": "Configuración de la CPU",
|
||||
"GPU Settings": "Configuración de GPU",
|
||||
"Display Settings": "Configuración de pantalla",
|
||||
"Experimental": "Experimental",
|
||||
"GPU Scheduling Override Method": "Método de anulación de programación de GPU",
|
||||
"can be dangerous and may cause": "puede ser peligroso y puede causar",
|
||||
"damage to your battery or charger!": "¡Daños a su batería o cargador!",
|
||||
"Charge Current Override": "Anulación de corriente de carga",
|
||||
"RAM Voltage Display Mode": "Modo de visualización de voltaje de RAM",
|
||||
"Polling Interval": "Intervalo de sondeo",
|
||||
"CPU Governor Minimum Frequency": "Frecuencia mínima del gobernador de CPU",
|
||||
"refresh rates may cause stress": "Las frecuencias de actualización pueden causar estrés.",
|
||||
"or damage to your display! ": "o daños a su pantalla!",
|
||||
"Proceed at your own risk!": "¡Continúe bajo su propio riesgo!",
|
||||
"Max Handheld Display": "Pantalla portátil máxima",
|
||||
"Display Clock": "Reloj de pantalla",
|
||||
"Official Rating": "Calificación oficial",
|
||||
"TDP Threshold": "Umbral de TDP",
|
||||
"Power": "poder",
|
||||
"Thermal Throttle Limit": "Límite del acelerador térmico",
|
||||
"HP Mode": "Modo HP",
|
||||
"Default (Mariko)": "Predeterminado (Mariko)",
|
||||
"Default (Erista)": "Predeterminado (Erista)",
|
||||
"Rating": "Calificación",
|
||||
"Safe Max (Mariko)": "Max seguro (Mariko)",
|
||||
"Safe Max (Erista)": "Safe Max (Erista)",
|
||||
"RAM VDD2 Voltage": "Voltaje RAM VDD2",
|
||||
"Voltage": "voltaje",
|
||||
"RAM VDDQ Voltage": "Voltaje RAM VDDQ",
|
||||
"RAM Frequency Editor": "Editor de frecuencia RAM",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "¡Se necesita alta velocidad!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333MHz (Necesita Speedo/PLL extremo)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366MHz (Necesita Speedo/PLL extremo)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400MHz (Necesita Speedo/PLL extremo)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (Necesita Speedo/PLL ridículo)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (Necesita Speedo/PLL ridículo)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz (Necesita Speedo/PLL ridículo)",
|
||||
"Ram Max Clock": "Ram Max Reloj",
|
||||
"RAM Latency Editor": "Editor de latencia de RAM",
|
||||
"RAM Timing Reductions": "Reducciones de tiempo de RAM",
|
||||
"Memory Timings": "Tiempos de memoria",
|
||||
"Advanced": "Avanzado",
|
||||
"t6 tRTW Fine Tune": "t6 tRTW Ajuste fino",
|
||||
"tRTW Fine Tune": "Ajuste fino tRTW",
|
||||
"t7 tWTR Fine Tune": "t7 tWTR Ajuste fino",
|
||||
"tWTR Fine Tune": "Ajuste fino de tWTR",
|
||||
"Memory Latencies": "Latencias de la memoria",
|
||||
"Read Latency": "Leer latencia",
|
||||
"Write Latency": "Latencia de escritura",
|
||||
"CPU Boost Clock": "Reloj de aumento de CPU",
|
||||
"CPU UV": "procesador ultravioleta",
|
||||
"CPU Unlock": "Desbloqueo de CPU",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "Voltaje máximo de la CPU",
|
||||
"CPU Max Clock": "Reloj máximo de CPU",
|
||||
"Extreme UV Table": "Mesa UV extrema",
|
||||
"CPU UV Table": "Tabla UV de CPU",
|
||||
"CPU Low UV": "CPU baja radiación ultravioleta",
|
||||
"CPU High UV": "CPU alta UV",
|
||||
"CPU Low VMIN": "VMIN bajo de CPU",
|
||||
"CPU High VMIN": "VMIN alto de CPU",
|
||||
"No Undervolt": "Sin subvoltaje",
|
||||
"SLT Table": "Mesa TR",
|
||||
"HiOPT Table": "Tabla HiOPT",
|
||||
"GPU Undervolt Table": "Tabla de subvoltaje de GPU",
|
||||
"GPU Minimum Voltage": "Voltaje mínimo de GPU",
|
||||
"Calculate GPU Vmin": "Calcular GPU Vmin",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "Voltaje máximo de GPU",
|
||||
"GPU Voltage Offset": "Compensación de voltaje de GPU",
|
||||
"Do not override": "no anular",
|
||||
"Enabled (Default)": "Habilitado (predeterminado)",
|
||||
"96.6% limit": "límite del 96,6%",
|
||||
"99.7% limit": "límite del 99,7%",
|
||||
"GPU Scheduling Override": "Anulación de programación de GPU",
|
||||
"Official Service": "Servicio Oficial",
|
||||
"GPU DVFS Mode": "Modo GPU DVFS",
|
||||
"GPU DVFS Offset": "Compensación DVFS de GPU",
|
||||
"GPU Voltage Table": "Tabla de voltaje de GPU",
|
||||
"GPU Custom Table (mV)": "Tabla personalizada de GPU (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075MHz sin UV, 1152MHz en SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "o 1228MHz en HiOPT pueden causar",
|
||||
"permanent damage to your Switch!": "¡Daño permanente a tu Switch!",
|
||||
"921MHz without UV and 960MHz on": "921MHz sin UV y 960MHz encendido",
|
||||
"SLT or HiOPT can cause ": "SLT o HiOPT pueden causar"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Informations",
|
||||
"IDDQ:": "IDDQ :",
|
||||
"Module: ": "Module :",
|
||||
"sys-dock status:": "état du dock système :",
|
||||
"SaltyNX status:": "Statut SaltyNX :",
|
||||
"RR Display status:": "Etat d'affichage RR :",
|
||||
"Wafer Position:": "Position de la plaquette :",
|
||||
"Credits": "Crédits",
|
||||
"Developers": "Développeurs",
|
||||
"Contributors": "Contributeurs",
|
||||
"Testers": "Testeurs",
|
||||
"Special Thanks": "Remerciements spéciaux",
|
||||
"Unknown": "Inconnu",
|
||||
"Installed": "Installé",
|
||||
"Not Installed": "Non installé",
|
||||
"X: %u Y: %u": "X : %u Y : %u",
|
||||
"THE BEER-WARE LICENSE": "LA LICENCE DE LA BIÈRE",
|
||||
"Default": "Par défaut",
|
||||
"Do Not Override": "Ne pas remplacer",
|
||||
"Disabled": "Désactivé",
|
||||
"Enabled": "Activé",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Réinitialiser",
|
||||
"Display": "Affichage",
|
||||
"Application changed\\n\\n": "Application modifiée\\n\\n",
|
||||
"The running application changed\\n\\n": "L'application en cours d'exécution a changé\\n\\n",
|
||||
"while editing was going on.": "pendant le montage.",
|
||||
"Board": "Conseil",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Impossible de se connecter au module système hoc-clk.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Veuillez vous assurer que tout est\\n\\n",
|
||||
"correctly installed and enabled.": "correctement installé et activé.",
|
||||
"Fatal error": "Erreur fatale",
|
||||
"Temporary Overrides ": "Remplacements temporaires",
|
||||
"Sleep Mode": "Mode veille",
|
||||
"Stock": "Actions",
|
||||
"Dev OC": "Développeur OC",
|
||||
"Boost Mode": "Mode Boost",
|
||||
"Safe Max": "Coffre-fort maximum",
|
||||
"Unsafe Max": "Dangereux Max",
|
||||
"Absolute Max": "Max absolu",
|
||||
"Handheld Safe Max": "Coffre-fort portatif Max",
|
||||
"Enable": "Activer",
|
||||
"Edit App Profile": "Modifier le profil de l'application",
|
||||
"Edit Global Profile": "Modifier le profil global",
|
||||
"Temporary Overrides": "Remplacements temporaires",
|
||||
"Settings": "Paramètres",
|
||||
"About": "À propos",
|
||||
"Compiling with minimal features": "Compilation avec des fonctionnalités minimales",
|
||||
"General Settings": "Paramètres généraux",
|
||||
"Governor Settings": "Paramètres du gouverneur",
|
||||
"Safety Settings": "Paramètres de sécurité",
|
||||
"Save KIP Settings": "Enregistrer les paramètres KIP",
|
||||
"RAM Settings": "Paramètres de la RAM",
|
||||
"CPU Settings": "Paramètres du processeur",
|
||||
"GPU Settings": "Paramètres du processeur graphique",
|
||||
"Display Settings": "Paramètres d'affichage",
|
||||
"Experimental": "Expérimental",
|
||||
"GPU Scheduling Override Method": "Méthode de remplacement de la planification GPU",
|
||||
"can be dangerous and may cause": "peut être dangereux et provoquer",
|
||||
"damage to your battery or charger!": "dommages à votre batterie ou à votre chargeur !",
|
||||
"Charge Current Override": "Remplacement du courant de charge",
|
||||
"RAM Voltage Display Mode": "Mode d'affichage de la tension de la RAM",
|
||||
"Polling Interval": "Intervalle d'interrogation",
|
||||
"CPU Governor Minimum Frequency": "Fréquence minimale du gouverneur du processeur",
|
||||
"refresh rates may cause stress": "les taux de rafraîchissement peuvent causer du stress",
|
||||
"or damage to your display! ": "ou endommager votre écran !",
|
||||
"Proceed at your own risk!": "Procédez à vos propres risques !",
|
||||
"Max Handheld Display": "Affichage portable maximum",
|
||||
"Display Clock": "Affichage de l'horloge",
|
||||
"Official Rating": "Classement officiel",
|
||||
"TDP Threshold": "Seuil TDP",
|
||||
"Power": "Puissance",
|
||||
"Thermal Throttle Limit": "Limite d'accélérateur thermique",
|
||||
"HP Mode": "Mode HP",
|
||||
"Default (Mariko)": "Par défaut (Mariko)",
|
||||
"Default (Erista)": "Par défaut (Erista)",
|
||||
"Rating": "Note",
|
||||
"Safe Max (Mariko)": "Coffre-fort Max (Mariko)",
|
||||
"Safe Max (Erista)": "Coffre-fort Max (Erista)",
|
||||
"RAM VDD2 Voltage": "Tension de la RAM VDD2",
|
||||
"Voltage": "Tension",
|
||||
"RAM VDDQ Voltage": "Tension VDDQ de la RAM",
|
||||
"RAM Frequency Editor": "Éditeur de fréquence RAM",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Besoin d'un speedo haut !",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (nécessite un Speedo/PLL extrême)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (nécessite un Speedo/PLL extrême)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (nécessite un Speedo/PLL extrême)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 MHz (nécessite un Speedo/PLL ridicule)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (nécessite un Speedo/PLL ridicule)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (nécessite un Speedo/PLL ridicule)",
|
||||
"Ram Max Clock": "Ram Max Horloge",
|
||||
"RAM Latency Editor": "Éditeur de latence RAM",
|
||||
"RAM Timing Reductions": "Réductions de synchronisation de la RAM",
|
||||
"Memory Timings": "Horaires de mémoire",
|
||||
"Advanced": "Avancé",
|
||||
"t6 tRTW Fine Tune": "t6 tRTW réglage fin",
|
||||
"tRTW Fine Tune": "tRTW Réglage fin",
|
||||
"t7 tWTR Fine Tune": "t7 tWTR réglage fin",
|
||||
"tWTR Fine Tune": "Réglage fin du tWTR",
|
||||
"Memory Latencies": "Latences de mémoire",
|
||||
"Read Latency": "Latence de lecture",
|
||||
"Write Latency": "Latence d'écriture",
|
||||
"CPU Boost Clock": "Horloge d'augmentation du processeur",
|
||||
"CPU UV": "UV du processeur",
|
||||
"CPU Unlock": "Déverrouillage du processeur",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "Tension maximale du processeur",
|
||||
"CPU Max Clock": "Horloge maximale du processeur",
|
||||
"Extreme UV Table": "Table UV Extrême",
|
||||
"CPU UV Table": "Tableau UV du processeur",
|
||||
"CPU Low UV": "CPU faible UV",
|
||||
"CPU High UV": "CPU UV élevé",
|
||||
"CPU Low VMIN": "CPU faible VMIN",
|
||||
"CPU High VMIN": "Processeur VMIN élevé",
|
||||
"No Undervolt": "Pas de sous-tension",
|
||||
"SLT Table": "Tableau SLT",
|
||||
"HiOPT Table": "Tableau HiOPT",
|
||||
"GPU Undervolt Table": "Tableau de sous-tension GPU",
|
||||
"GPU Minimum Voltage": "Tension minimale du GPU",
|
||||
"Calculate GPU Vmin": "Calculer la Vmin du GPU",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "Tension maximale du GPU",
|
||||
"GPU Voltage Offset": "Décalage de tension du GPU",
|
||||
"Do not override": "Ne remplacez pas",
|
||||
"Enabled (Default)": "Activé (par défaut)",
|
||||
"96.6% limit": "Limite de 96,6 %",
|
||||
"99.7% limit": "Limite de 99,7 %",
|
||||
"GPU Scheduling Override": "Remplacement de la planification GPU",
|
||||
"Official Service": "Service officiel",
|
||||
"GPU DVFS Mode": "Mode GPU DVFS",
|
||||
"GPU DVFS Offset": "Décalage GPU DVFS",
|
||||
"GPU Voltage Table": "Tableau de tension du GPU",
|
||||
"GPU Custom Table (mV)": "Tableau personnalisé GPU (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075 MHz sans UV, 1152 MHz sur SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "ou 1228 MHz sur HiOPT peut provoquer",
|
||||
"permanent damage to your Switch!": "dommages permanents à votre Switch !",
|
||||
"921MHz without UV and 960MHz on": "921 MHz sans UV et 960 MHz activé",
|
||||
"SLT or HiOPT can cause ": "SLT ou HiOPT peuvent provoquer"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Informazioni",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Modulo:",
|
||||
"sys-dock status:": "stato di sys-dock",
|
||||
"SaltyNX status:": "Stato di SaltyNX:",
|
||||
"RR Display status:": "Stato del RR:",
|
||||
"Wafer Position:": "Posizione nel Wafer:",
|
||||
"Credits": "Crediti",
|
||||
"Developers": "Sviluppatori",
|
||||
"Contributors": "Collaboratori",
|
||||
"Testers": "Tester",
|
||||
"Special Thanks": "Un Ringraziamento Speciale",
|
||||
"Unknown": "Sconosciuto",
|
||||
"Installed": "Installato",
|
||||
"Not Installed": "Non installato",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "THE BEER-WARE LICENSE",
|
||||
"Default": "Predefinito",
|
||||
"Do Not Override": "Non Sovrascrivere",
|
||||
"Disabled": "Disabilitato",
|
||||
"Enabled": "Abilitato",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Ripristina",
|
||||
"Display": "Schermo",
|
||||
"Application changed\\n\\n": "Applicazione modificata\\n\\n",
|
||||
"The running application changed\\n\\n": "L'applicazione in esecuzione è cambiata\\n\\n",
|
||||
"while editing was going on.": "mentre era in corso la modifica.",
|
||||
"Board": "Scheda",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Impossibile connettersi al sysmodule hoc-clk.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Assicurati che tutto sia\\n\\n",
|
||||
"correctly installed and enabled.": "correttamente installato e abilitato.",
|
||||
"Fatal error": "Errore fatale",
|
||||
"Temporary Overrides ": "Sostituzioni Temporanee",
|
||||
"Sleep Mode": "Modalità di Sospensione",
|
||||
"Stock": "Originale",
|
||||
"Dev OC": "OC dev",
|
||||
"Boost Mode": "Modalità Boost",
|
||||
"Safe Max": "Massimo Sicuro",
|
||||
"Unsafe Max": "Massimo Non Sicuro",
|
||||
"Absolute Max": "Massimo Assoluto",
|
||||
"Handheld Safe Max": "Massimo Sicuro Modalità Portatile",
|
||||
"Enable": "Abilita",
|
||||
"Edit App Profile": "Modifica Profilo Dell'App",
|
||||
"Edit Global Profile": "Modifica Profilo Globale",
|
||||
"Temporary Overrides": "Sostituzioni Temporanee",
|
||||
"Settings": "Impostazioni",
|
||||
"About": "A Riguardo Di",
|
||||
"Compiling with minimal features": "Compilazione con funzionalità minime",
|
||||
"General Settings": "Impostazioni Generali",
|
||||
"Governor Settings": "Impostazioni Del Governor",
|
||||
"Safety Settings": "Impostazioni Di Sicurezza",
|
||||
"Save KIP Settings": "Salva le impostazioni del KIP",
|
||||
"RAM Settings": "Impostazioni della RAM",
|
||||
"CPU Settings": "Impostazioni della CPU",
|
||||
"GPU Settings": "Impostazioni della GPU",
|
||||
"Display Settings": "Impostazioni dello Schermo",
|
||||
"Experimental": "Sperimentale",
|
||||
"GPU Scheduling Override Method": "Metodo di override dello scheduling GPU",
|
||||
"can be dangerous and may cause": "può essere pericoloso e può causare",
|
||||
"damage to your battery or charger!": "danni alla batteria o al caricabatterie!",
|
||||
"Charge Current Override": "Override della Corrente di Carica",
|
||||
"RAM Voltage Display Mode": "Modalità di Visualizzazione della Tensione RAM",
|
||||
"Polling Interval": "Intervallo di polling",
|
||||
"CPU Governor Minimum Frequency": "Frequenza minima del Governor della CPU",
|
||||
"refresh rates may cause stress": "le frequenze di aggiornamento possono causare stress",
|
||||
"or damage to your display! ": "o danni al display!",
|
||||
"Proceed at your own risk!": "Procedi a tuo rischio e pericolo!",
|
||||
"Max Handheld Display": "Display Massimo in Modalità Portatile",
|
||||
"Display Clock": "Frequenza del Display",
|
||||
"Official Rating": "Rating Ufficiale",
|
||||
"TDP Threshold": "Soglia TDP",
|
||||
"Power": "Potenza",
|
||||
"Thermal Throttle Limit": "Limite Termico",
|
||||
"HP Mode": "Modalità HP",
|
||||
"Default (Mariko)": "Predefinito (Mariko)",
|
||||
"Default (Erista)": "Predefinito (Erista)",
|
||||
"Rating": "Valutazione",
|
||||
"Safe Max (Mariko)": "Massimo Sicuro (Mariko)",
|
||||
"Safe Max (Erista)": "Massimo Sicuro (Erista)",
|
||||
"RAM VDD2 Voltage": "Tensione RAM VDD2",
|
||||
"Voltage": "Voltaggio",
|
||||
"RAM VDDQ Voltage": "Voltaggio VDDQ della RAM",
|
||||
"RAM Frequency Editor": "Editor della frequenza RAM",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Alto Valore Speedo Necessario!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (richiede Speedo/PLL altissimo)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (richiede Speedo/PLL altissimo)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (richiede Speedo/PLL altissimo)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 MHz (richiede Speedo/PLL estremo)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (richiede Speedo/PLL estremo)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (richiede Speedo/PLL estremo)",
|
||||
"Ram Max Clock": "Frequenza Massima Ram",
|
||||
"RAM Latency Editor": "Editor della Latenza RAM",
|
||||
"RAM Timing Reductions": "Riduzioni dei Timing della RAM",
|
||||
"Memory Timings": "Timing di Memoria",
|
||||
"Advanced": "Avanzato",
|
||||
"t6 tRTW Fine Tune": "Regolazione Fine t6 tRTW",
|
||||
"tRTW Fine Tune": "Regolazione Fine tRTW",
|
||||
"t7 tWTR Fine Tune": "Regolazione Fine t7 tWTR",
|
||||
"tWTR Fine Tune": "Regolazione Fine tWTR",
|
||||
"Memory Latencies": "Latenza della Memoria",
|
||||
"Read Latency": "Latenza di Lettura",
|
||||
"Write Latency": "Latenza di Scrittura",
|
||||
"CPU Boost Clock": "Frequenza CPU in Boost",
|
||||
"CPU UV": "Undervolt CPU",
|
||||
"CPU Unlock": "Sblocco della CPU",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "Voltaggio massimo della CPU",
|
||||
"CPU Max Clock": "Frequenza massima della CPU",
|
||||
"Extreme UV Table": "Tabella UV estremo",
|
||||
"CPU UV Table": "Tabella UV della CPU",
|
||||
"CPU Low UV": "CPU UV Bassa Frequenza",
|
||||
"CPU High UV": "CPU UV Alta Frequenza",
|
||||
"CPU Low VMIN": "CPU VMIN Bassa Frequenza",
|
||||
"CPU High VMIN": "CPU VMIN Alta Frequenza",
|
||||
"No Undervolt": "Nessun Undervolt",
|
||||
"SLT Table": "Tabella SLT",
|
||||
"HiOPT Table": "Tabella HiOPT",
|
||||
"GPU Undervolt Table": "Tabella di Undervolt GPU",
|
||||
"GPU Minimum Voltage": "Voltaggio Minimo della GPU",
|
||||
"Calculate GPU Vmin": "Calcola GPU Vmin",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "Voltaggio massimo della GPU",
|
||||
"GPU Voltage Offset": "Offset di Voltaggio della GPU",
|
||||
"Do not override": "Non sovrascrivere",
|
||||
"Enabled (Default)": "Abilitato (impostazione predefinita)",
|
||||
"96.6% limit": "Limite del 96,6%.",
|
||||
"99.7% limit": "Limite del 99,7%.",
|
||||
"GPU Scheduling Override": "Override dello Scheduling GPU",
|
||||
"Official Service": "Servizio ufficiale",
|
||||
"GPU DVFS Mode": "Modalità DVFS GPU",
|
||||
"GPU DVFS Offset": "Offset DVFS della GPU",
|
||||
"GPU Voltage Table": "Tabella delle Tensioni della GPU",
|
||||
"GPU Custom Table (mV)": "Tabella GPU Personalizzata (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075 MHz senza UV, 1152 MHz su SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "o 1228 MHz su HiOPT possono causare",
|
||||
"permanent damage to your Switch!": "danni permanenti alla tua Switch!",
|
||||
"921MHz without UV and 960MHz on": "921 MHz senza UV e 960 MHz su",
|
||||
"SLT or HiOPT can cause ": "SLT o HiOPT possono causare"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "情報",
|
||||
"IDDQ:": "IDQ:",
|
||||
"Module: ": "モジュール:",
|
||||
"sys-dock status:": "システムドックのステータス:",
|
||||
"SaltyNX status:": "SaltyNX ステータス:",
|
||||
"RR Display status:": "RR 表示ステータス:",
|
||||
"Wafer Position:": "ウェーハの位置:",
|
||||
"Credits": "クレジット",
|
||||
"Developers": "開発者",
|
||||
"Contributors": "貢献者",
|
||||
"Testers": "テスター",
|
||||
"Special Thanks": "特別な感謝の気持ち",
|
||||
"Unknown": "不明",
|
||||
"Installed": "インストール済み",
|
||||
"Not Installed": "インストールされていません",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "ビール製品ライセンス",
|
||||
"Default": "デフォルト",
|
||||
"Do Not Override": "上書きしないでください",
|
||||
"Disabled": "障害者",
|
||||
"Enabled": "有効",
|
||||
" \\ue0e3 Reset": "\\ue0e3 リセット",
|
||||
"Display": "ディスプレイ",
|
||||
"Application changed\\n\\n": "アプリケーションが変更されました\\n\\n",
|
||||
"The running application changed\\n\\n": "実行中のアプリケーションが変更されました\\n\\n",
|
||||
"while editing was going on.": "編集を進めている最中でした。",
|
||||
"Board": "理事会",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "hoc-clk sysmodule に接続できませんでした。\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "すべてが正しいことを確認してください\\n\\n",
|
||||
"correctly installed and enabled.": "正しくインストールされ、有効になっています。",
|
||||
"Fatal error": "致命的なエラー",
|
||||
"Temporary Overrides ": "一時的なオーバーライド",
|
||||
"Sleep Mode": "スリープモード",
|
||||
"Stock": "在庫",
|
||||
"Dev OC": "開発OC",
|
||||
"Boost Mode": "ブーストモード",
|
||||
"Safe Max": "セーフマックス",
|
||||
"Unsafe Max": "危険なマックス",
|
||||
"Absolute Max": "絶対最大値",
|
||||
"Handheld Safe Max": "手持ち金庫マックス",
|
||||
"Enable": "有効にする",
|
||||
"Edit App Profile": "アプリプロファイルの編集",
|
||||
"Edit Global Profile": "グローバルプロファイルの編集",
|
||||
"Temporary Overrides": "一時的なオーバーライド",
|
||||
"Settings": "設定",
|
||||
"About": "について",
|
||||
"Compiling with minimal features": "最小限の機能でコンパイルする",
|
||||
"General Settings": "一般設定",
|
||||
"Governor Settings": "ガバナーの設定",
|
||||
"Safety Settings": "安全設定",
|
||||
"Save KIP Settings": "KIP 設定の保存",
|
||||
"RAM Settings": "RAM設定",
|
||||
"CPU Settings": "CPUの設定",
|
||||
"GPU Settings": "GPU設定",
|
||||
"Display Settings": "表示設定",
|
||||
"Experimental": "実験的",
|
||||
"GPU Scheduling Override Method": "GPU スケジューリング オーバーライド メソッド",
|
||||
"can be dangerous and may cause": "危険であり、原因となる可能性があります",
|
||||
"damage to your battery or charger!": "バッテリーまたは充電器が損傷します。",
|
||||
"Charge Current Override": "充電電流オーバーライド",
|
||||
"RAM Voltage Display Mode": "RAM電圧表示モード",
|
||||
"Polling Interval": "ポーリング間隔",
|
||||
"CPU Governor Minimum Frequency": "CPU ガバナの最小周波数",
|
||||
"refresh rates may cause stress": "リフレッシュレートがストレスを引き起こす可能性がある",
|
||||
"or damage to your display! ": "ディスプレイに損傷を与えてしまいます。",
|
||||
"Proceed at your own risk!": "自己責任で進めてください!",
|
||||
"Max Handheld Display": "最大ハンドヘルドディスプレイ",
|
||||
"Display Clock": "時計の表示",
|
||||
"Official Rating": "公式評価",
|
||||
"TDP Threshold": "TDP しきい値",
|
||||
"Power": "パワー",
|
||||
"Thermal Throttle Limit": "サーマルスロットル制限",
|
||||
"HP Mode": "HPモード",
|
||||
"Default (Mariko)": "デフォルト(マリコ)",
|
||||
"Default (Erista)": "デフォルト(エリスタ)",
|
||||
"Rating": "評価",
|
||||
"Safe Max (Mariko)": "セーフマックス(マリコ)",
|
||||
"Safe Max (Erista)": "セーフマックス(エリスタ)",
|
||||
"RAM VDD2 Voltage": "RAM VDD2 電圧",
|
||||
"Voltage": "電圧",
|
||||
"RAM VDDQ Voltage": "RAM VDDQ 電圧",
|
||||
"RAM Frequency Editor": "RAM周波数エディター",
|
||||
"JEDEC.": "JEDEC。",
|
||||
"High speedo needed!": "ハイスピードが必要です!",
|
||||
"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 が必要)",
|
||||
"Ram Max Clock": "ラムマックスクロック",
|
||||
"RAM Latency Editor": "RAM レイテンシ エディター",
|
||||
"RAM Timing Reductions": "RAM タイミングの削減",
|
||||
"Memory Timings": "メモリタイミング",
|
||||
"Advanced": "上級者向け",
|
||||
"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": "書き込みレイテンシ",
|
||||
"CPU Boost Clock": "CPUブーストクロック",
|
||||
"CPU UV": "CPU UV",
|
||||
"CPU Unlock": "CPUロック解除",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "CPU最大電圧",
|
||||
"CPU Max Clock": "CPU最大クロック",
|
||||
"Extreme UV Table": "エクストリーム UV テーブル",
|
||||
"CPU UV Table": "CPU UV テーブル",
|
||||
"CPU Low UV": "CPU 低 UV",
|
||||
"CPU High UV": "CPU 高紫外線",
|
||||
"CPU Low VMIN": "CPU 低 VMIN",
|
||||
"CPU High VMIN": "CPU の高い VMIN",
|
||||
"No Undervolt": "不足電圧なし",
|
||||
"SLT Table": "SLTテーブル",
|
||||
"HiOPT Table": "HiOPT テーブル",
|
||||
"GPU Undervolt Table": "GPUアンダーボルトテーブル",
|
||||
"GPU Minimum Voltage": "GPUの最小電圧",
|
||||
"Calculate GPU Vmin": "GPU Vmin を計算する",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "GPU最大電圧",
|
||||
"GPU Voltage Offset": "GPU電圧オフセット",
|
||||
"Do not override": "上書きしないでください",
|
||||
"Enabled (Default)": "有効 (デフォルト)",
|
||||
"96.6% limit": "96.6%制限",
|
||||
"99.7% limit": "99.7%制限",
|
||||
"GPU Scheduling Override": "GPU スケジュールのオーバーライド",
|
||||
"Official Service": "正式サービス",
|
||||
"GPU DVFS Mode": "GPU DVFS モード",
|
||||
"GPU DVFS Offset": "GPU DVFS オフセット",
|
||||
"GPU Voltage Table": "GPU電圧テーブル",
|
||||
"GPU Custom Table (mV)": "GPUカスタムテーブル(mV)",
|
||||
"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": "921MHz(UVなし)、960MHz(UVあり)",
|
||||
"SLT or HiOPT can cause ": "SLT または HiOPT が原因となる可能性があります"
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
{
|
||||
"Information": "Information",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Module:",
|
||||
"sys-dock status:": "sys-dock status:",
|
||||
"SaltyNX status:": "SaltyNX status:",
|
||||
"RR Display status:": "RR Display status:",
|
||||
"Wafer Position:": "Wafer Position:",
|
||||
"Credits": "Credits",
|
||||
"Developers": "Developers",
|
||||
"Contributors": "Contributors",
|
||||
"Testers": "Testers",
|
||||
"Special Thanks": "Special Thanks",
|
||||
"Unknown": "Unknown",
|
||||
"Installed": "Installed",
|
||||
"Not Installed": "Not Installed",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "THE BEER-WARE LICENSE",
|
||||
"Default": "Default",
|
||||
"Do Not Override": "Do Not Override",
|
||||
"Disabled": "Disabled",
|
||||
"Enabled": "Enabled",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Reset",
|
||||
"Display": "Display",
|
||||
"Application changed\\n\\n": "Application changed\\n\\n",
|
||||
"The running application changed\\n\\n": "The running application changed\\n\\n",
|
||||
"while editing was going on.": "while editing was going on.",
|
||||
"App ID": "App ID",
|
||||
"Profile": "Profile",
|
||||
"Board": "Board",
|
||||
"USB Charger": "USB Charger",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Could not connect to hoc-clk sysmodule.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Please make sure everything is\\n\\n",
|
||||
"correctly installed and enabled.": "correctly installed and enabled.",
|
||||
"Fatal error": "Fatal error",
|
||||
"Temporary Overrides ": "Temporary Overrides",
|
||||
"Sleep Mode": "Sleep Mode",
|
||||
"Stock": "Stock",
|
||||
"Dev OC": "Dev OC",
|
||||
"Boost Mode": "Boost Mode",
|
||||
"Safe Max": "Safe Max",
|
||||
"Unsafe Max": "Unsafe Max",
|
||||
"Absolute Max": "Absolute Max",
|
||||
"Handheld": "Handheld",
|
||||
"Handheld Safe Max": "Handheld Safe Max",
|
||||
"Docked": "Docked",
|
||||
"Enable": "Enable",
|
||||
"Edit App Profile": "Edit App Profile",
|
||||
"Edit Global Profile": "Edit Global Profile",
|
||||
"Temporary Overrides": "Temporary Overrides",
|
||||
"Settings": "Settings",
|
||||
"About": "About",
|
||||
"Compiling with minimal features": "Compiling with minimal features",
|
||||
"General Settings": "General Settings",
|
||||
"Governor Settings": "Governor Settings",
|
||||
"Safety Settings": "Safety Settings",
|
||||
"Save KIP Settings": "Save KIP Settings",
|
||||
"RAM Settings": "RAM Settings",
|
||||
"CPU Settings": "CPU Settings",
|
||||
"GPU Settings": "GPU Settings",
|
||||
"Display Settings": "Display Settings",
|
||||
"Experimental": "Experimental",
|
||||
"GPU Scheduling Override Method": "GPU Scheduling Override Method",
|
||||
"can be dangerous and may cause": "can be dangerous and may cause",
|
||||
"damage to your battery or charger!": "damage to your battery or charger!",
|
||||
"Charge Current Override": "Charge Current Override",
|
||||
"RAM Voltage Display Mode": "RAM Voltage Display Mode",
|
||||
"Polling Interval": "Polling Interval",
|
||||
"CPU Governor Minimum Frequency": "CPU Governor Minimum Frequency",
|
||||
"refresh rates may cause stress": "refresh rates may cause stress",
|
||||
"or damage to your display! ": "or damage to your display!",
|
||||
"Proceed at your own risk!": "Proceed at your own risk!",
|
||||
"Max Handheld Display": "Max Handheld Display",
|
||||
"Display Clock": "Display Clock",
|
||||
"Official Rating": "Official Rating",
|
||||
"TDP Threshold": "TDP Threshold",
|
||||
"Power": "Power",
|
||||
"Thermal Throttle Limit": "Thermal Throttle Limit",
|
||||
"HP Mode": "HP Mode",
|
||||
"Default (Mariko)": "Default (Mariko)",
|
||||
"Default (Erista)": "Default (Erista)",
|
||||
"Rating": "Rating",
|
||||
"Safe Max (Mariko)": "Safe Max (Mariko)",
|
||||
"Safe Max (Erista)": "Safe Max (Erista)",
|
||||
"RAM VDD2 Voltage": "RAM VDD2 Voltage",
|
||||
"Voltage": "Voltage",
|
||||
"RAM VDDQ Voltage": "RAM VDDQ Voltage",
|
||||
"RAM Frequency Editor": "RAM Frequency Editor",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333MHz (Needs extreme Speedo/PLL)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366MHz (Needs extreme Speedo/PLL)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400MHz (Needs extreme Speedo/PLL)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (Needs ridiculous Speedo/PLL)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (Needs ridiculous Speedo/PLL)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz (Needs ridiculous Speedo/PLL)",
|
||||
"Ram Max Clock": "Ram Max Clock",
|
||||
"RAM Latency Editor": "RAM Latency Editor",
|
||||
"RAM Timing Reductions": "RAM Timing Reductions",
|
||||
"Memory Timings": "Memory Timings",
|
||||
"tREFI": "tREFI",
|
||||
"Advanced": "Advanced",
|
||||
"t6 tRTW Fine Tune": "t6 tRTW Fine Tune",
|
||||
"tRTW Fine Tune": "tRTW Fine Tune",
|
||||
"t7 tWTR Fine Tune": "t7 tWTR Fine Tune",
|
||||
"tWTR Fine Tune": "tWTR Fine Tune",
|
||||
"Memory Latencies": "Memory Latencies",
|
||||
"Read Latency": "Read Latency",
|
||||
"Write Latency": "Write Latency",
|
||||
"CPU Boost Clock": "CPU Boost Clock",
|
||||
"CPU UV": "CPU UV",
|
||||
"CPU Unlock": "CPU Unlock",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "CPU Max Voltage",
|
||||
"CPU Max Clock": "CPU Max Clock",
|
||||
"Extreme UV Table": "Extreme UV Table",
|
||||
"CPU UV Table": "CPU UV Table",
|
||||
"CPU Low UV": "CPU Low UV",
|
||||
"CPU High UV": "CPU High UV",
|
||||
"CPU Low VMIN": "CPU Low VMIN",
|
||||
"CPU High VMIN": "CPU High VMIN",
|
||||
"No Undervolt": "No Undervolt",
|
||||
"SLT Table": "SLT Table",
|
||||
"HiOPT Table": "HiOPT Table",
|
||||
"GPU Undervolt Table": "GPU Undervolt Table",
|
||||
"GPU Minimum Voltage": "GPU Minimum Voltage",
|
||||
"Calculate GPU Vmin": "Calculate GPU Vmin",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "GPU Maximum Voltage",
|
||||
"GPU Voltage Offset": "GPU Voltage Offset",
|
||||
"Do not override": "Do not override",
|
||||
"Enabled (Default)": "Enabled (Default)",
|
||||
"96.6% limit": "96.6% limit",
|
||||
"99.7% limit": "99.7% limit",
|
||||
"GPU Scheduling Override": "GPU Scheduling Override",
|
||||
"Official Service": "Official Service",
|
||||
"GPU DVFS Mode": "GPU DVFS Mode",
|
||||
"GPU DVFS Offset": "GPU DVFS Offset",
|
||||
"GPU Voltage Table": "GPU Voltage Table",
|
||||
"GPU Custom Table (mV)": "GPU Custom Table (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075MHz without UV, 1152MHz on SLT",
|
||||
"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"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "정보",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "모듈:",
|
||||
"sys-dock status:": "sys-dock 상태:",
|
||||
"SaltyNX status:": "SaltyNX 상태:",
|
||||
"RR Display status:": "RR 표시 상태:",
|
||||
"Wafer Position:": "웨이퍼 위치:",
|
||||
"Credits": "크레딧",
|
||||
"Developers": "개발자",
|
||||
"Contributors": "기여자",
|
||||
"Testers": "테스터",
|
||||
"Special Thanks": "특별한 분",
|
||||
"Unknown": "알 수 없음",
|
||||
"Installed": "설치됨",
|
||||
"Not Installed": "설치되지 않음",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "맥주 제품 라이센스",
|
||||
"Default": "기본값",
|
||||
"Do Not Override": "재정의하지 마십시오",
|
||||
"Disabled": "비활성화",
|
||||
"Enabled": "활성화됨",
|
||||
" \\ue0e3 Reset": "\\ue0e3 재설정",
|
||||
"Display": "디스플레이",
|
||||
"Application changed\\n\\n": "애플리케이션이 변경되었습니다.\\n\\n",
|
||||
"The running application changed\\n\\n": "실행 중인 애플리케이션이 변경되었습니다.\\n\\n",
|
||||
"while editing was going on.": "편집이 진행되는 동안.",
|
||||
"Board": "보드",
|
||||
"%u.%u%u mV": "%u.%u%umV",
|
||||
"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": "치명적인 오류",
|
||||
"Temporary Overrides ": "임시 재정의",
|
||||
"Sleep Mode": "절전 모드",
|
||||
"Stock": "주식",
|
||||
"Dev OC": "개발 OC",
|
||||
"Overwrite Boost Mode": "부스트 모드 덮어쓰기",
|
||||
"Safe Max": "안전함 최대값",
|
||||
"Unsafe Max": "불안정 최대값",
|
||||
"Absolute Max": "절대 최대값",
|
||||
"Handheld Safe Max": "휴대모드 안전함 최대값",
|
||||
"Enable": "활성화",
|
||||
"Edit App Profile": "앱 프로필 편집",
|
||||
"Edit Global Profile": "글로벌 프로필 편집",
|
||||
"Temporary Overrides": "임시 재정의",
|
||||
"Settings": "설정",
|
||||
"About": "소개",
|
||||
"Compiling with minimal features": "최소한의 기능으로 컴파일하기",
|
||||
"General Settings": "일반 설정",
|
||||
"Governor Settings": "거버너 설정",
|
||||
"Safety Settings": "안전 설정",
|
||||
"Save KIP Settings": "KIP 설정 저장",
|
||||
"RAM Settings": "RAM 설정",
|
||||
"CPU Settings": "CPU 설정",
|
||||
"GPU Settings": "GPU 설정",
|
||||
"Display Settings": "디스플레이 설정",
|
||||
"Experimental": "실험적",
|
||||
"GPU Scheduling Override Method": "GPU 스케줄링 재정의 방법",
|
||||
"can be dangerous and may cause": "위험할 수 있고 원인이 될 수 있습니다.",
|
||||
"damage to your battery or charger!": "배터리나 충전기가 손상되었습니다!",
|
||||
"Charge Current Override": "충전 전류 오버라이드",
|
||||
"RAM Voltage Display Mode": "RAM 전압 표시 모드",
|
||||
"Polling Interval": "폴링 간격",
|
||||
"CPU Governor Minimum Frequency": "CPU 거버너 최소 주파수",
|
||||
"refresh rates may cause stress": "디스플레이 주사율 빈도 변경은",
|
||||
"or damage to your display! ": "기기에 손상이 발생될 수 있습니다!",
|
||||
"Proceed at your own risk!": "책임하에 주의해서 사용하십시오!",
|
||||
"Max Handheld Display": "최대 휴대용 디스플레이",
|
||||
"Display Clock": "디스플레이 클럭",
|
||||
"Official Rating": "공식 등급",
|
||||
"TDP Threshold": "TDP 임계값",
|
||||
"Power": "힘",
|
||||
"Thermal Throttle Limit": "열 스로틀 한계",
|
||||
"HP Mode": "HP 모드",
|
||||
"Default (Mariko)": "기본값(마리코)",
|
||||
"Default (Erista)": "기본값(에리스타)",
|
||||
"Rating": "표준값",
|
||||
"Safe Max (Mariko)": "안전함 최대치(마리코)",
|
||||
"Safe Max (Erista)": "안전함 최대치(에리스타)",
|
||||
"RAM VDD2 Voltage": "RAM VDD2 전압",
|
||||
"Voltage": "전압",
|
||||
"RAM VDDQ Voltage": "RAM VDDQ 전압",
|
||||
"RAM Frequency Editor": "RAM 주파수 편집기",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "높은 스피도값이 필요합니다!",
|
||||
"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 필요)",
|
||||
"Ram Max Clock": "RAM 최대 클럭",
|
||||
"RAM Latency Editor": "RAM 지연 시간 편집기",
|
||||
"RAM Timing Reductions": "RAM 타이밍 편집기",
|
||||
"Memory Timings": "메모리 타이밍",
|
||||
"Advanced": "고급",
|
||||
"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": "쓰기 지연 시간",
|
||||
"CPU Boost Clock": "CPU 부스트 클럭",
|
||||
"CPU UV": "CPU 언더볼트",
|
||||
"CPU Unlock": "CPU 잠금 해제",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"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 언더볼트 테이블",
|
||||
"GPU Minimum Voltage": "GPU 최소 전압",
|
||||
"Calculate GPU Vmin": "GPU Vmin 계산",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "GPU 최대 전압",
|
||||
"GPU Voltage Offset": "GPU 전압 오프셋",
|
||||
"Do not override": "재정의하지 않음",
|
||||
"Enabled (Default)": "활성화됨(기본값)",
|
||||
"96.6% limit": "96.6% 한도",
|
||||
"99.7% limit": "99.7% 한도",
|
||||
"GPU Scheduling Override": "GPU 스케줄링 재정의",
|
||||
"Official Service": "공식 서비스",
|
||||
"GPU DVFS Mode": "GPU DVFS 모드",
|
||||
"GPU DVFS Offset": "GPU DVFS 오프셋",
|
||||
"GPU Voltage Table": "GPU 전압 테이블",
|
||||
"GPU Custom Table (mV)": "GPU 사용자 정의 테이블(mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "UV 없이 1075MHz, SLT에서 1152MHz",
|
||||
"or 1228MHz on HiOPT can cause ": "또는 HiOPT에서 1228MHz를 사용하면",
|
||||
"permanent damage to your Switch!": "스위치가 영구적으로 손상될 수 있습니다!",
|
||||
"921MHz without UV and 960MHz on": "UV가 없는 경우 921MHz, 켜진 경우에는 960MHz",
|
||||
"SLT or HiOPT can cause ": "SLT 또는 HiOPT는 다음을 유발할 수 있습니다."
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Informatie",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "module:",
|
||||
"sys-dock status:": "sys-dock-status:",
|
||||
"SaltyNX status:": "SaltyNX-status:",
|
||||
"RR Display status:": "RR Weergavestatus:",
|
||||
"Wafer Position:": "Waferpositie:",
|
||||
"Credits": "Kredieten",
|
||||
"Developers": "Ontwikkelaars",
|
||||
"Contributors": "Bijdragers",
|
||||
"Testers": "Testers",
|
||||
"Special Thanks": "Speciale dank",
|
||||
"Unknown": "Onbekend",
|
||||
"Installed": "Geïnstalleerd",
|
||||
"Not Installed": "Niet geïnstalleerd",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "DE LICENTIE VOOR BIERWAREN",
|
||||
"Default": "Standaard",
|
||||
"Do Not Override": "Niet overschrijven",
|
||||
"Disabled": "Uitgeschakeld",
|
||||
"Enabled": "Ingeschakeld",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Opnieuw instellen",
|
||||
"Display": "Weergave",
|
||||
"Application changed\\n\\n": "Applicatie gewijzigd\\n\\n",
|
||||
"The running application changed\\n\\n": "De actieve applicatie is gewijzigd\\n\\n",
|
||||
"while editing was going on.": "terwijl er werd bewerkt.",
|
||||
"Board": "Bord",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Kan geen verbinding maken met hoc-clk sysmodule.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Zorg ervoor dat alles in orde is\\n\\n",
|
||||
"correctly installed and enabled.": "correct geïnstalleerd en ingeschakeld.",
|
||||
"Fatal error": "Fatale fout",
|
||||
"Temporary Overrides ": "Tijdelijke overschrijvingen",
|
||||
"Sleep Mode": "Slaapmodus",
|
||||
"Stock": "Voorraad",
|
||||
"Dev OC": "Ontwikkelaar OC",
|
||||
"Boost Mode": "Boost-modus",
|
||||
"Safe Max": "Veilig Max",
|
||||
"Unsafe Max": "OnveiligMax",
|
||||
"Absolute Max": "Absoluut Max",
|
||||
"Handheld Safe Max": "Handkluis Max",
|
||||
"Enable": "Inschakelen",
|
||||
"Edit App Profile": "App-profiel bewerken",
|
||||
"Edit Global Profile": "Globaal profiel bewerken",
|
||||
"Temporary Overrides": "Tijdelijke overschrijvingen",
|
||||
"Settings": "Instellingen",
|
||||
"About": "Over",
|
||||
"Compiling with minimal features": "Compileren met minimale functies",
|
||||
"General Settings": "Algemene instellingen",
|
||||
"Governor Settings": "Gouverneur instellingen",
|
||||
"Safety Settings": "Veiligheidsinstellingen",
|
||||
"Save KIP Settings": "Sla KIP-instellingen op",
|
||||
"RAM Settings": "RAM-instellingen",
|
||||
"CPU Settings": "CPU-instellingen",
|
||||
"GPU Settings": "GPU-instellingen",
|
||||
"Display Settings": "Weergave-instellingen",
|
||||
"Experimental": "Experimenteel",
|
||||
"GPU Scheduling Override Method": "Methode voor het overschrijven van GPU-planning",
|
||||
"can be dangerous and may cause": "kan gevaarlijk zijn en kan veroorzaken",
|
||||
"damage to your battery or charger!": "schade aan uw accu of lader!",
|
||||
"Charge Current Override": "Laadstroom overschrijven",
|
||||
"RAM Voltage Display Mode": "Weergavemodus RAM-spanning",
|
||||
"Polling Interval": "Polling-interval",
|
||||
"CPU Governor Minimum Frequency": "Minimale frequentie CPU-regelaar",
|
||||
"refresh rates may cause stress": "vernieuwingsfrequenties kunnen stress veroorzaken",
|
||||
"or damage to your display! ": "of schade aan uw display!",
|
||||
"Proceed at your own risk!": "Ga verder op eigen risico!",
|
||||
"Max Handheld Display": "Maximaal handheld-display",
|
||||
"Display Clock": "Klok weergeven",
|
||||
"Official Rating": "Officiële beoordeling",
|
||||
"TDP Threshold": "TDP-drempel",
|
||||
"Power": "Macht",
|
||||
"Thermal Throttle Limit": "Thermische gaslimiet",
|
||||
"HP Mode": "HP-modus",
|
||||
"Default (Mariko)": "Standaard (Mariko)",
|
||||
"Default (Erista)": "Standaard (Erista)",
|
||||
"Rating": "Beoordeling",
|
||||
"Safe Max (Mariko)": "Veilig Max (Mariko)",
|
||||
"Safe Max (Erista)": "Veilige Max (Erista)",
|
||||
"RAM VDD2 Voltage": "RAM VDD2-spanning",
|
||||
"Voltage": "Spanning",
|
||||
"RAM VDDQ Voltage": "RAM VDDQ-spanning",
|
||||
"RAM Frequency Editor": "RAM-frequentie-editor",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Hoge snelheid nodig!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (vereist extreme snelheidsmeter/PLL)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (vereist extreme snelheidsmeter/PLL)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (vereist extreme snelheidsmeter/PLL)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (heeft een belachelijke snelheidsmeter/PLL nodig)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (heeft een belachelijke snelheidsmeter/PLL nodig)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (heeft een belachelijke snelheidsmeter/PLL nodig)",
|
||||
"Ram Max Clock": "Ram Max-klok",
|
||||
"RAM Latency Editor": "RAM-latentie-editor",
|
||||
"RAM Timing Reductions": "RAM-timingreducties",
|
||||
"Memory Timings": "Geheugentijden",
|
||||
"Advanced": "Geavanceerd",
|
||||
"t6 tRTW Fine Tune": "t6 tRTW Fijnafstemming",
|
||||
"tRTW Fine Tune": "tRTW Fijnafstemming",
|
||||
"t7 tWTR Fine Tune": "t7 tWTR Fijnafstemming",
|
||||
"tWTR Fine Tune": "tWTR Fijnafstemming",
|
||||
"Memory Latencies": "Geheugenlatenties",
|
||||
"Read Latency": "Lees Latentie",
|
||||
"Write Latency": "Schrijf latentie",
|
||||
"CPU Boost Clock": "CPU-boostklok",
|
||||
"CPU UV": "CPU-UV",
|
||||
"CPU Unlock": "CPU-ontgrendeling",
|
||||
"CPU VMIN": "CPU-VMIN",
|
||||
"CPU Max Voltage": "Maximale CPU-spanning",
|
||||
"CPU Max Clock": "CPU maximale klok",
|
||||
"Extreme UV Table": "Extreme UV-tafel",
|
||||
"CPU UV Table": "CPU UV-tabel",
|
||||
"CPU Low UV": "CPU Lage UV",
|
||||
"CPU High UV": "CPU Hoge UV",
|
||||
"CPU Low VMIN": "CPU Lage VMIN",
|
||||
"CPU High VMIN": "CPU Hoge VMIN",
|
||||
"No Undervolt": "Geen ondervolt",
|
||||
"SLT Table": "SLT-tabel",
|
||||
"HiOPT Table": "HiOPT-tabel",
|
||||
"GPU Undervolt Table": "GPU-undervolttabel",
|
||||
"GPU Minimum Voltage": "GPU-minimale spanning",
|
||||
"Calculate GPU Vmin": "Bereken GPU Vmin",
|
||||
"GPU VMIN": "GPU-VMIN",
|
||||
"GPU Maximum Voltage": "GPU maximale spanning",
|
||||
"GPU Voltage Offset": "GPU-spanningsoffset",
|
||||
"Do not override": "Niet overschrijven",
|
||||
"Enabled (Default)": "Ingeschakeld (standaard)",
|
||||
"96.6% limit": "96,6% limiet",
|
||||
"99.7% limit": "99,7% limiet",
|
||||
"GPU Scheduling Override": "GPU-planning negeren",
|
||||
"Official Service": "Officiële dienst",
|
||||
"GPU DVFS Mode": "GPU DVFS-modus",
|
||||
"GPU DVFS Offset": "GPU DVFS-offset",
|
||||
"GPU Voltage Table": "GPU-spanningstabel",
|
||||
"GPU Custom Table (mV)": "Aangepaste GPU-tabel (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075MHz zonder UV, 1152MHz op SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "of 1228MHz op HiOPT kan dit veroorzaken",
|
||||
"permanent damage to your Switch!": "blijvende schade aan uw Switch!",
|
||||
"921MHz without UV and 960MHz on": "921MHz zonder UV en 960MHz aan",
|
||||
"SLT or HiOPT can cause ": "SLT of HiOPT kunnen dit veroorzaken"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Informacje",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Moduł:",
|
||||
"sys-dock status:": "stan sys-dock:",
|
||||
"SaltyNX status:": "Stan SaltyNX:",
|
||||
"RR Display status:": "Stan wyświetlacza:",
|
||||
"Wafer Position:": "Pozycja wafla:",
|
||||
"Credits": "Kredyty",
|
||||
"Developers": "Deweloperzy",
|
||||
"Contributors": "Współautorzy",
|
||||
"Testers": "Testery",
|
||||
"Special Thanks": "Specjalne podziękowania",
|
||||
"Unknown": "Nieznany",
|
||||
"Installed": "Zainstalowany",
|
||||
"Not Installed": "Nie zainstalowano",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "LICENCJA NA WYROBY PIWNE",
|
||||
"Default": "Domyślne",
|
||||
"Do Not Override": "Nie zastępuj",
|
||||
"Disabled": "Niepełnosprawny",
|
||||
"Enabled": "Włączone",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Zresetuj",
|
||||
"Display": "Wyświetlacz",
|
||||
"Application changed\\n\\n": "Aplikacja została zmieniona\\n\\n",
|
||||
"The running application changed\\n\\n": "Działająca aplikacja została zmieniona\\n\\n",
|
||||
"while editing was going on.": "podczas gdy edycja była w toku.",
|
||||
"Board": "Deska",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Nie można połączyć się z modułem sysmodule hoc-clk.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Upewnij się, że wszystko jest\\n\\n",
|
||||
"correctly installed and enabled.": "poprawnie zainstalowany i włączony.",
|
||||
"Fatal error": "Fatalny błąd",
|
||||
"Temporary Overrides ": "Tymczasowe nadpisania",
|
||||
"Sleep Mode": "Tryb uśpienia",
|
||||
"Stock": "Zapas",
|
||||
"Dev OC": "Dev OC",
|
||||
"Boost Mode": "Tryb wzmocnienia",
|
||||
"Safe Max": "Bezpieczny maks",
|
||||
"Unsafe Max": "Niebezpieczny maks",
|
||||
"Absolute Max": "Absolutny maks",
|
||||
"Handheld Safe Max": "Sejf ręczny Max",
|
||||
"Enable": "Włącz",
|
||||
"Edit App Profile": "Edytuj profil aplikacji",
|
||||
"Edit Global Profile": "Edytuj profil globalny",
|
||||
"Temporary Overrides": "Tymczasowe nadpisania",
|
||||
"Settings": "Ustawienia",
|
||||
"About": "O",
|
||||
"Compiling with minimal features": "Kompilacja z minimalnymi funkcjami",
|
||||
"General Settings": "Ustawienia ogólne",
|
||||
"Governor Settings": "Ustawienia gubernatora",
|
||||
"Safety Settings": "Ustawienia bezpieczeństwa",
|
||||
"Save KIP Settings": "Zapisz ustawienia KIP",
|
||||
"RAM Settings": "Ustawienia pamięci RAM",
|
||||
"CPU Settings": "Ustawienia procesora",
|
||||
"GPU Settings": "Ustawienia GPU",
|
||||
"Display Settings": "Ustawienia wyświetlania",
|
||||
"Experimental": "Eksperymentalny",
|
||||
"GPU Scheduling Override Method": "Metoda obejścia harmonogramu GPU",
|
||||
"can be dangerous and may cause": "może być niebezpieczne i powodować",
|
||||
"damage to your battery or charger!": "uszkodzenie akumulatora lub ładowarki!",
|
||||
"Charge Current Override": "Obejście prądu ładowania",
|
||||
"RAM Voltage Display Mode": "Tryb wyświetlania napięcia RAM",
|
||||
"Polling Interval": "Interwał odpytywania",
|
||||
"CPU Governor Minimum Frequency": "Minimalna częstotliwość regulatora procesora",
|
||||
"refresh rates may cause stress": "częstotliwości odświeżania mogą powodować stres",
|
||||
"or damage to your display! ": "lub uszkodzenie wyświetlacza!",
|
||||
"Proceed at your own risk!": "Postępuj na własne ryzyko!",
|
||||
"Max Handheld Display": "Maksymalny wyświetlacz ręczny",
|
||||
"Display Clock": "Wyświetl zegar",
|
||||
"Official Rating": "Oficjalna ocena",
|
||||
"TDP Threshold": "Próg TDP",
|
||||
"Power": "Moc",
|
||||
"Thermal Throttle Limit": "Limit przepustnicy termicznej",
|
||||
"HP Mode": "Tryb HP",
|
||||
"Default (Mariko)": "Domyślny (Mariko)",
|
||||
"Default (Erista)": "Domyślny (Erista)",
|
||||
"Rating": "Ocena",
|
||||
"Safe Max (Mariko)": "Bezpieczny Max (Mariko)",
|
||||
"Safe Max (Erista)": "Bezpieczny Max (Erista)",
|
||||
"RAM VDD2 Voltage": "Napięcie pamięci RAM VDD2",
|
||||
"Voltage": "Napięcie",
|
||||
"RAM VDDQ Voltage": "Napięcie RAM VDDQ",
|
||||
"RAM Frequency Editor": "Edytor częstotliwości RAM",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Potrzebna duża prędkość!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (wymaga ekstremalnego Speedo/PLL)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (wymaga ekstremalnego Speedo/PLL)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (wymaga ekstremalnego Speedo/PLL)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 MHz (potrzebuje śmiesznego Speedo/PLL)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (potrzebuje śmiesznego Speedo/PLL)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (potrzebuje śmiesznego Speedo/PLL)",
|
||||
"Ram Max Clock": "Zegar Ram Max",
|
||||
"RAM Latency Editor": "Edytor opóźnień pamięci RAM",
|
||||
"RAM Timing Reductions": "Zmniejszenie taktowania pamięci RAM",
|
||||
"Memory Timings": "Taktowanie pamięci",
|
||||
"Advanced": "Zaawansowane",
|
||||
"t6 tRTW Fine Tune": "t6 tRTW Dostrój",
|
||||
"tRTW Fine Tune": "tRTW Dostosuj",
|
||||
"t7 tWTR Fine Tune": "t7 tWTR Dostosuj",
|
||||
"tWTR Fine Tune": "tWTR Dostosuj",
|
||||
"Memory Latencies": "Opóźnienia pamięci",
|
||||
"Read Latency": "Przeczytaj Opóźnienie",
|
||||
"Write Latency": "Opóźnienie zapisu",
|
||||
"CPU Boost Clock": "Zegar wzmocnienia procesora",
|
||||
"CPU UV": "Procesor UV",
|
||||
"CPU Unlock": "Odblokowanie procesora",
|
||||
"CPU VMIN": "Procesor VMIN",
|
||||
"CPU Max Voltage": "Maksymalne napięcie procesora",
|
||||
"CPU Max Clock": "Maks. zegar procesora",
|
||||
"Extreme UV Table": "Ekstremalny stół UV",
|
||||
"CPU UV Table": "Tabela UV procesora",
|
||||
"CPU Low UV": "Niskie promieniowanie UV procesora",
|
||||
"CPU High UV": "Wysokie promieniowanie UV procesora",
|
||||
"CPU Low VMIN": "Niski poziom VMIN procesora",
|
||||
"CPU High VMIN": "Wysoki poziom VMIN procesora",
|
||||
"No Undervolt": "Brak Undervolta",
|
||||
"SLT Table": "Stół SLT",
|
||||
"HiOPT Table": "Stół HiOPT",
|
||||
"GPU Undervolt Table": "Tabela niedoboru napięcia GPU",
|
||||
"GPU Minimum Voltage": "Minimalne napięcie procesora graficznego",
|
||||
"Calculate GPU Vmin": "Oblicz Vmin GPU",
|
||||
"GPU VMIN": "VMIN GPU",
|
||||
"GPU Maximum Voltage": "Maksymalne napięcie procesora graficznego",
|
||||
"GPU Voltage Offset": "Przesunięcie napięcia GPU",
|
||||
"Do not override": "Nie zastępuj",
|
||||
"Enabled (Default)": "Włączone (domyślnie)",
|
||||
"96.6% limit": "Limit 96,6%.",
|
||||
"99.7% limit": "Limit 99,7%.",
|
||||
"GPU Scheduling Override": "Zastąpienie harmonogramu GPU",
|
||||
"Official Service": "Oficjalny serwis",
|
||||
"GPU DVFS Mode": "Tryb DVFS procesora graficznego",
|
||||
"GPU DVFS Offset": "Przesunięcie DVFS GPU",
|
||||
"GPU Voltage Table": "Tabela napięć GPU",
|
||||
"GPU Custom Table (mV)": "Tabela niestandardowa GPU (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075 MHz bez UV, 1152 MHz na SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "lub 1228 MHz na HiOPT może powodować",
|
||||
"permanent damage to your Switch!": "trwałe uszkodzenie Switcha!",
|
||||
"921MHz without UV and 960MHz on": "921 MHz bez UV i 960 MHz włączone",
|
||||
"SLT or HiOPT can cause ": "Przyczyną mogą być SLT lub HiOPT"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Informação",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Módulo:",
|
||||
"sys-dock status:": "status do dock do sistema:",
|
||||
"SaltyNX status:": "Status do SaltyNX:",
|
||||
"RR Display status:": "Status de exibição do RR:",
|
||||
"Wafer Position:": "Posição da bolacha:",
|
||||
"Credits": "Créditos",
|
||||
"Developers": "Desenvolvedores",
|
||||
"Contributors": "Colaboradores",
|
||||
"Testers": "Testadores",
|
||||
"Special Thanks": "Agradecimentos especiais",
|
||||
"Unknown": "Desconhecido",
|
||||
"Installed": "Instalado",
|
||||
"Not Installed": "Não instalado",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "A LICENÇA DE CERVEJA",
|
||||
"Default": "Padrão",
|
||||
"Do Not Override": "Não substituir",
|
||||
"Disabled": "Desativado",
|
||||
"Enabled": "Habilitado",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Redefinir",
|
||||
"Display": "Exibição",
|
||||
"Application changed\\n\\n": "Aplicativo alterado\\n\\n",
|
||||
"The running application changed\\n\\n": "O aplicativo em execução foi alterado\\n\\n",
|
||||
"while editing was going on.": "enquanto a edição estava acontecendo.",
|
||||
"Board": "Conselho",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Não foi possível conectar-se ao sysmodule hoc-clk.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Verifique se tudo está\\n\\n",
|
||||
"correctly installed and enabled.": "corretamente instalado e ativado.",
|
||||
"Fatal error": "Erro fatal",
|
||||
"Temporary Overrides ": "Substituições temporárias",
|
||||
"Sleep Mode": "Modo de suspensão",
|
||||
"Stock": "Estoque",
|
||||
"Dev OC": "Desenvolvedor OC",
|
||||
"Boost Mode": "Modo de reforço",
|
||||
"Safe Max": "Máx. Seguro",
|
||||
"Unsafe Max": "Máximo inseguro",
|
||||
"Absolute Max": "Máximo absoluto",
|
||||
"Handheld Safe Max": "Portátil Seguro Máx.",
|
||||
"Enable": "Habilitar",
|
||||
"Edit App Profile": "Editar perfil do aplicativo",
|
||||
"Edit Global Profile": "Editar perfil global",
|
||||
"Temporary Overrides": "Substituições temporárias",
|
||||
"Settings": "Configurações",
|
||||
"About": "Sobre",
|
||||
"Compiling with minimal features": "Compilando com recursos mínimos",
|
||||
"General Settings": "Configurações Gerais",
|
||||
"Governor Settings": "Configurações do Governador",
|
||||
"Safety Settings": "Configurações de segurança",
|
||||
"Save KIP Settings": "Salvar configurações KIP",
|
||||
"RAM Settings": "Configurações de RAM",
|
||||
"CPU Settings": "Configurações de CPU",
|
||||
"GPU Settings": "Configurações de GPU",
|
||||
"Display Settings": "Configurações de exibição",
|
||||
"Experimental": "Experimental",
|
||||
"GPU Scheduling Override Method": "Método de substituição de agendamento de GPU",
|
||||
"can be dangerous and may cause": "pode ser perigoso e causar",
|
||||
"damage to your battery or charger!": "danos à sua bateria ou carregador!",
|
||||
"Charge Current Override": "Substituição de corrente de carga",
|
||||
"RAM Voltage Display Mode": "Modo de exibição de tensão RAM",
|
||||
"Polling Interval": "Intervalo de votação",
|
||||
"CPU Governor Minimum Frequency": "Frequência Mínima do Governador da CPU",
|
||||
"refresh rates may cause stress": "taxas de atualização podem causar estresse",
|
||||
"or damage to your display! ": "ou danos ao seu monitor!",
|
||||
"Proceed at your own risk!": "Prossiga por sua conta e risco!",
|
||||
"Max Handheld Display": "Visor portátil máximo",
|
||||
"Display Clock": "Exibir relógio",
|
||||
"Official Rating": "Classificação Oficial",
|
||||
"TDP Threshold": "Limite de TDP",
|
||||
"Power": "Poder",
|
||||
"Thermal Throttle Limit": "Limite de aceleração térmica",
|
||||
"HP Mode": "Modo HP",
|
||||
"Default (Mariko)": "Padrão (Mariko)",
|
||||
"Default (Erista)": "Padrão (Erista)",
|
||||
"Rating": "Avaliação",
|
||||
"Safe Max (Mariko)": "Máximo Seguro (Mariko)",
|
||||
"Safe Max (Erista)": "Seguro Max (Erista)",
|
||||
"RAM VDD2 Voltage": "Tensão RAM VDD2",
|
||||
"Voltage": "Tensão",
|
||||
"RAM VDDQ Voltage": "Tensão RAM VDDQ",
|
||||
"RAM Frequency Editor": "Editor de frequência RAM",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Alta velocidade necessária!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333MHz (precisa de Speedo/PLL extremo)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (precisa de Speedo/PLL extremo)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (precisa de Speedo/PLL extremo)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (precisa de Speedo/PLL ridículo)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (precisa de Speedo/PLL ridículo)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (precisa de Speedo/PLL ridículo)",
|
||||
"Ram Max Clock": "Relógio máximo de Ram",
|
||||
"RAM Latency Editor": "Editor de latência de RAM",
|
||||
"RAM Timing Reductions": "Reduções de tempo de RAM",
|
||||
"Memory Timings": "Tempos de memória",
|
||||
"Advanced": "Avançado",
|
||||
"t6 tRTW Fine Tune": "t6 tRTW Ajuste fino",
|
||||
"tRTW Fine Tune": "Ajuste fino tRTW",
|
||||
"t7 tWTR Fine Tune": "t7 tWTR Ajuste fino",
|
||||
"tWTR Fine Tune": "Ajuste fino tWTR",
|
||||
"Memory Latencies": "Latências de memória",
|
||||
"Read Latency": "Latência de leitura",
|
||||
"Write Latency": "Latência de gravação",
|
||||
"CPU Boost Clock": "Relógio de aumento da CPU",
|
||||
"CPU UV": "UV da CPU",
|
||||
"CPU Unlock": "Desbloqueio da CPU",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "Tensão máxima da CPU",
|
||||
"CPU Max Clock": "Relógio máximo da CPU",
|
||||
"Extreme UV Table": "Mesa UV Extrema",
|
||||
"CPU UV Table": "Tabela UV da CPU",
|
||||
"CPU Low UV": "UV baixo da CPU",
|
||||
"CPU High UV": "CPU alta UV",
|
||||
"CPU Low VMIN": "CPU baixa VMIN",
|
||||
"CPU High VMIN": "VMIN alto da CPU",
|
||||
"No Undervolt": "Sem subtensão",
|
||||
"SLT Table": "Tabela SLT",
|
||||
"HiOPT Table": "Tabela HiOPT",
|
||||
"GPU Undervolt Table": "Tabela de subtensão da GPU",
|
||||
"GPU Minimum Voltage": "Tensão mínima da GPU",
|
||||
"Calculate GPU Vmin": "Calcular Vmin da GPU",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "Tensão máxima da GPU",
|
||||
"GPU Voltage Offset": "Compensação de tensão da GPU",
|
||||
"Do not override": "Não substitua",
|
||||
"Enabled (Default)": "Habilitado (padrão)",
|
||||
"96.6% limit": "Limite de 96,6%",
|
||||
"99.7% limit": "Limite de 99,7%",
|
||||
"GPU Scheduling Override": "Substituição de agendamento de GPU",
|
||||
"Official Service": "Serviço Oficial",
|
||||
"GPU DVFS Mode": "Modo GPU DVFS",
|
||||
"GPU DVFS Offset": "Deslocamento DVFS da GPU",
|
||||
"GPU Voltage Table": "Tabela de tensão da GPU",
|
||||
"GPU Custom Table (mV)": "Tabela personalizada de GPU (mV)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075 MHz sem UV, 1152 MHz em SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "ou 1228 MHz em HiOPT pode causar",
|
||||
"permanent damage to your Switch!": "danos permanentes ao seu Switch!",
|
||||
"921MHz without UV and 960MHz on": "921 MHz sem UV e 960 MHz ativado",
|
||||
"SLT or HiOPT can cause ": "SLT ou HiOPT podem causar"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Информация",
|
||||
"IDDQ:": "ИДДК:",
|
||||
"Module: ": "Модуль:",
|
||||
"sys-dock status:": "Статус системной док-станции:",
|
||||
"SaltyNX status:": "Статус SaltyNX:",
|
||||
"RR Display status:": "Статус отображения RR:",
|
||||
"Wafer Position:": "Позиция вафли:",
|
||||
"Credits": "Кредиты",
|
||||
"Developers": "Разработчики",
|
||||
"Contributors": "Авторы",
|
||||
"Testers": "Тестеры",
|
||||
"Special Thanks": "Особая благодарность",
|
||||
"Unknown": "Неизвестно",
|
||||
"Installed": "Установлено",
|
||||
"Not Installed": "Не установлено",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "ЛИЦЕНЗИЯ НА ПРОДАЖУ ПИВА",
|
||||
"Default": "По умолчанию",
|
||||
"Do Not Override": "Не переопределять",
|
||||
"Disabled": "Отключено",
|
||||
"Enabled": "Включено",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Сброс",
|
||||
"Display": "Дисплей",
|
||||
"Application changed\\n\\n": "Приложение изменено\\n\\n",
|
||||
"The running application changed\\n\\n": "Запущенное приложение изменилось\\n\\n",
|
||||
"while editing was going on.": "пока шло редактирование.",
|
||||
"Board": "Совет",
|
||||
"%u.%u%u mV": "%u.%u%u мВ",
|
||||
"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": "Неустранимая ошибка",
|
||||
"Temporary Overrides ": "Временные переопределения",
|
||||
"Sleep Mode": "Спящий режим",
|
||||
"Stock": "Акции",
|
||||
"Dev OC": "Разработчик OC",
|
||||
"Boost Mode": "Режим повышения",
|
||||
"Safe Max": "Сейф Макс",
|
||||
"Unsafe Max": "Небезопасный Макс",
|
||||
"Absolute Max": "Абсолютный Макс",
|
||||
"Handheld Safe Max": "Ручной сейф Макс",
|
||||
"Enable": "Включить",
|
||||
"Edit App Profile": "Редактировать профиль приложения",
|
||||
"Edit Global Profile": "Редактировать глобальный профиль",
|
||||
"Temporary Overrides": "Временные переопределения",
|
||||
"Settings": "Настройки",
|
||||
"About": "О",
|
||||
"Compiling with minimal features": "Компиляция с минимальными возможностями",
|
||||
"General Settings": "Общие настройки",
|
||||
"Governor Settings": "Настройки губернатора",
|
||||
"Safety Settings": "Настройки безопасности",
|
||||
"Save KIP Settings": "Сохранить настройки КИП",
|
||||
"RAM Settings": "Настройки ОЗУ",
|
||||
"CPU Settings": "Настройки процессора",
|
||||
"GPU Settings": "Настройки графического процессора",
|
||||
"Display Settings": "Настройки дисплея",
|
||||
"Experimental": "Экспериментальный",
|
||||
"GPU Scheduling Override Method": "Метод переопределения планирования графического процессора",
|
||||
"can be dangerous and may cause": "может быть опасным и может вызвать",
|
||||
"damage to your battery or charger!": "повреждение аккумулятора или зарядного устройства!",
|
||||
"Charge Current Override": "Блокировка зарядного тока",
|
||||
"RAM Voltage Display Mode": "Режим отображения напряжения ОЗУ",
|
||||
"Polling Interval": "Интервал опроса",
|
||||
"CPU Governor Minimum Frequency": "Минимальная частота регулятора ЦП",
|
||||
"refresh rates may cause stress": "частота обновления может вызвать стресс",
|
||||
"or damage to your display! ": "или повреждение дисплея!",
|
||||
"Proceed at your own risk!": "Действуйте на свой страх и риск!",
|
||||
"Max Handheld Display": "Макс. портативный дисплей",
|
||||
"Display Clock": "Дисплей Часы",
|
||||
"Official Rating": "Официальный рейтинг",
|
||||
"TDP Threshold": "Порог TDP",
|
||||
"Power": "Мощность",
|
||||
"Thermal Throttle Limit": "Температурный предел дроссельной заслонки",
|
||||
"HP Mode": "Режим HP",
|
||||
"Default (Mariko)": "По умолчанию (Марико)",
|
||||
"Default (Erista)": "По умолчанию (Эриста)",
|
||||
"Rating": "Рейтинг",
|
||||
"Safe Max (Mariko)": "Сейф Макс (Марико)",
|
||||
"Safe Max (Erista)": "Сейф Макс (Эриста)",
|
||||
"RAM VDD2 Voltage": "Напряжение ОЗУ VDD2",
|
||||
"Voltage": "Напряжение",
|
||||
"RAM VDDQ Voltage": "Напряжение ОЗУ VDDQ",
|
||||
"RAM Frequency Editor": "Редактор частоты оперативной памяти",
|
||||
"JEDEC.": "ДЖЕДЕК.",
|
||||
"High speedo needed!": "Нужен высокий спидометр!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 МГц (требуется экстремальный спидометр/PLL)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 МГц (требуется экстремальный спидометр/PLL)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 МГц (требуется экстремальный спидометр/PLL)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 МГц (нужен нелепый спидометр/PLL)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 МГц (нужен нелепый спидометр/PLL)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 МГц (нужен нелепый спидометр/PLL)",
|
||||
"Ram Max Clock": "Рам Макс Часы",
|
||||
"RAM Latency Editor": "Редактор задержки оперативной памяти",
|
||||
"RAM Timing Reductions": "Сокращение таймингов ОЗУ",
|
||||
"Memory Timings": "Тайминги памяти",
|
||||
"Advanced": "Расширенный",
|
||||
"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": "Задержка записи",
|
||||
"CPU Boost Clock": "Тактовая частота процессора",
|
||||
"CPU UV": "УФ процессора",
|
||||
"CPU Unlock": "Разблокировка процессора",
|
||||
"CPU VMIN": "ЦП VMIN",
|
||||
"CPU Max Voltage": "Максимальное напряжение процессора",
|
||||
"CPU Max Clock": "Максимальная частота процессора",
|
||||
"Extreme UV Table": "Стол для экстремального УФ-излучения",
|
||||
"CPU UV Table": "UV-таблица процессора",
|
||||
"CPU Low UV": "ЦП с низким УФ-излучением",
|
||||
"CPU High UV": "Процессор с высоким УФ",
|
||||
"CPU Low VMIN": "Низкий VMIN процессора",
|
||||
"CPU High VMIN": "Высокий VMIN процессора",
|
||||
"No Undervolt": "Нет Андервольта",
|
||||
"SLT Table": "Таблица ТА",
|
||||
"HiOPT Table": "Таблица HiOPT",
|
||||
"GPU Undervolt Table": "Таблица пониженного напряжения графического процессора",
|
||||
"GPU Minimum Voltage": "Минимальное напряжение графического процессора",
|
||||
"Calculate GPU Vmin": "Рассчитать Vmin графического процессора",
|
||||
"GPU VMIN": "Вмин графического процессора",
|
||||
"GPU Maximum Voltage": "Максимальное напряжение графического процессора",
|
||||
"GPU Voltage Offset": "Смещение напряжения графического процессора",
|
||||
"Do not override": "Не переопределять",
|
||||
"Enabled (Default)": "Включено (по умолчанию)",
|
||||
"96.6% limit": "Предел 96,6%",
|
||||
"99.7% limit": "лимит 99,7%",
|
||||
"GPU Scheduling Override": "Переопределение планирования графического процессора",
|
||||
"Official Service": "Официальная служба",
|
||||
"GPU DVFS Mode": "Режим графического процессора DVFS",
|
||||
"GPU DVFS Offset": "Смещение DVFS графического процессора",
|
||||
"GPU Voltage Table": "Таблица напряжений графического процессора",
|
||||
"GPU Custom Table (mV)": "Пользовательская таблица графического процессора (мВ)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075 МГц без УФ, 1152 МГц на SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "или 1228 МГц на HiOPT может привести к",
|
||||
"permanent damage to your Switch!": "необратимое повреждение вашего коммутатора!",
|
||||
"921MHz without UV and 960MHz on": "921 МГц без УФ и 960 МГц с включенным",
|
||||
"SLT or HiOPT can cause ": "SLT или HiOPT могут вызвать"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Інформація",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Модуль:",
|
||||
"sys-dock status:": "стан sys-dock:",
|
||||
"SaltyNX status:": "Статус SaltyNX:",
|
||||
"RR Display status:": "Статус дисплея RR:",
|
||||
"Wafer Position:": "Позиція пластини:",
|
||||
"Credits": "Кредити",
|
||||
"Developers": "Розробники",
|
||||
"Contributors": "Дописувачі",
|
||||
"Testers": "Тестери",
|
||||
"Special Thanks": "Особлива подяка",
|
||||
"Unknown": "Невідомий",
|
||||
"Installed": "встановлено",
|
||||
"Not Installed": "Не встановлено",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "ЛІЦЕНЗІЯ НА ПИВНИЙ ПОСУД",
|
||||
"Default": "За замовчуванням",
|
||||
"Do Not Override": "Не перевизначати",
|
||||
"Disabled": "Вимкнено",
|
||||
"Enabled": "Увімкнено",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Скидання",
|
||||
"Display": "Дисплей",
|
||||
"Application changed\\n\\n": "Додаток змінено\\n\\n",
|
||||
"The running application changed\\n\\n": "Запущена програма змінена\\n\\n",
|
||||
"while editing was going on.": "поки йшло редагування.",
|
||||
"Board": "дошка",
|
||||
"%u.%u%u mV": "%u.%u%u мВ",
|
||||
"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": "Фатальна помилка",
|
||||
"Temporary Overrides ": "Тимчасові перевизначення",
|
||||
"Sleep Mode": "Режим сну",
|
||||
"Stock": "Запас",
|
||||
"Dev OC": "Розробник OC",
|
||||
"Boost Mode": "Режим посилення",
|
||||
"Safe Max": "Безпечний макс",
|
||||
"Unsafe Max": "Небезпечний макс",
|
||||
"Absolute Max": "Абсолютний макс",
|
||||
"Handheld Safe Max": "Портативний сейф Макс",
|
||||
"Enable": "Увімкнути",
|
||||
"Edit App Profile": "Редагувати профіль програми",
|
||||
"Edit Global Profile": "Редагувати глобальний профіль",
|
||||
"Temporary Overrides": "Тимчасові перевизначення",
|
||||
"Settings": "Налаштування",
|
||||
"About": "про",
|
||||
"Compiling with minimal features": "Компіляція з мінімальними можливостями",
|
||||
"General Settings": "Загальні налаштування",
|
||||
"Governor Settings": "Налаштування губернатора",
|
||||
"Safety Settings": "Налаштування безпеки",
|
||||
"Save KIP Settings": "Зберегти налаштування KIP",
|
||||
"RAM Settings": "Налаштування оперативної пам'яті",
|
||||
"CPU Settings": "Налаштування ЦП",
|
||||
"GPU Settings": "Налаштування GPU",
|
||||
"Display Settings": "Налаштування дисплея",
|
||||
"Experimental": "Експериментальний",
|
||||
"GPU Scheduling Override Method": "Метод перевизначення планування GPU",
|
||||
"can be dangerous and may cause": "може бути небезпечним і може спричинити",
|
||||
"damage to your battery or charger!": "пошкодження акумулятора або зарядного пристрою!",
|
||||
"Charge Current Override": "Перевизначення струму заряду",
|
||||
"RAM Voltage Display Mode": "Режим відображення напруги RAM",
|
||||
"Polling Interval": "Інтервал опитування",
|
||||
"CPU Governor Minimum Frequency": "Мінімальна частота регулятора ЦП",
|
||||
"refresh rates may cause stress": "частоти оновлення можуть викликати стрес",
|
||||
"or damage to your display! ": "або пошкодження дисплея!",
|
||||
"Proceed at your own risk!": "Продовжуйте на свій страх і ризик!",
|
||||
"Max Handheld Display": "Максимальний портативний дисплей",
|
||||
"Display Clock": "Відображення годинника",
|
||||
"Official Rating": "Офіційний рейтинг",
|
||||
"TDP Threshold": "Поріг TDP",
|
||||
"Power": "потужність",
|
||||
"Thermal Throttle Limit": "Термічний дросельний ліміт",
|
||||
"HP Mode": "Режим HP",
|
||||
"Default (Mariko)": "За замовчуванням (Маріко)",
|
||||
"Default (Erista)": "За замовчуванням (Erista)",
|
||||
"Rating": "Рейтинг",
|
||||
"Safe Max (Mariko)": "Сейф Макс (Маріко)",
|
||||
"Safe Max (Erista)": "Сейф Макс (Еріста)",
|
||||
"RAM VDD2 Voltage": "Напруга RAM VDD2",
|
||||
"Voltage": "Напруга",
|
||||
"RAM VDDQ Voltage": "Напруга RAM VDDQ",
|
||||
"RAM Frequency Editor": "Редактор частоти оперативної пам'яті",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Потрібна висока швидкість!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 МГц (потрібна екстремальна швидкість/PLL)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 МГц (потрібна екстремальна швидкість/PLL)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 МГц (потрібна екстремальна швидкість/PLL)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 МГц (потрібен смішний Speedo/PLL)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 МГц (потрібен смішний Speedo/PLL)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 МГц (потрібен смішний Speedo/PLL)",
|
||||
"Ram Max Clock": "Годинник Ram Max",
|
||||
"RAM Latency Editor": "Редактор затримки оперативної пам'яті",
|
||||
"RAM Timing Reductions": "Скорочення оперативної пам'яті",
|
||||
"Memory Timings": "Таймінг пам'яті",
|
||||
"Advanced": "Просунутий",
|
||||
"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": "Затримка запису",
|
||||
"CPU Boost Clock": "CPU Boost Clock",
|
||||
"CPU UV": "CPU UV",
|
||||
"CPU Unlock": "Розблокування ЦП",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "Максимальна напруга ЦП",
|
||||
"CPU Max Clock": "Максимальна частота ЦП",
|
||||
"Extreme UV Table": "Екстремальний ультрафіолетовий стіл",
|
||||
"CPU UV Table": "CPU UV Таблиця",
|
||||
"CPU Low UV": "CPU Low UV",
|
||||
"CPU High UV": "CPU High UV",
|
||||
"CPU Low VMIN": "CPU Low VMIN",
|
||||
"CPU High VMIN": "CPU High VMIN",
|
||||
"No Undervolt": "Без андервольта",
|
||||
"SLT Table": "Таблиця SLT",
|
||||
"HiOPT Table": "Таблиця HiOPT",
|
||||
"GPU Undervolt Table": "Таблиця зниження напруги GPU",
|
||||
"GPU Minimum Voltage": "Мінімальна напруга GPU",
|
||||
"Calculate GPU Vmin": "Розрахувати GPU Vmin",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "Максимальна напруга GPU",
|
||||
"GPU Voltage Offset": "Зсув напруги GPU",
|
||||
"Do not override": "Не перевизначати",
|
||||
"Enabled (Default)": "Увімкнено (за замовчуванням)",
|
||||
"96.6% limit": "96,6% обмеження",
|
||||
"99.7% limit": "Обмеження 99,7%.",
|
||||
"GPU Scheduling Override": "Перевизначення планування GPU",
|
||||
"Official Service": "Офіційний сервіс",
|
||||
"GPU DVFS Mode": "Режим GPU DVFS",
|
||||
"GPU DVFS Offset": "GPU DVFS Offset",
|
||||
"GPU Voltage Table": "Таблиця напруги GPU",
|
||||
"GPU Custom Table (mV)": "Спеціальна таблиця GPU (мВ)",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075 МГц без УФ, 1152 МГц на SLT",
|
||||
"or 1228MHz on HiOPT can cause ": "або 1228 МГц на HiOPT може спричинити",
|
||||
"permanent damage to your Switch!": "незворотне пошкодження вашого комутатора!",
|
||||
"921MHz without UV and 960MHz on": "921 МГц без УФ і 960 МГц увімкнено",
|
||||
"SLT or HiOPT can cause ": "SLT або HiOPT можуть спричинити"
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
{
|
||||
"Information": "信息",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "模块: ",
|
||||
"sys-dock status:": "sys-dock 状态:",
|
||||
"SaltyNX status:": "SaltyNX 状态:",
|
||||
"RR Display status:": "RR 显示状态:",
|
||||
"Wafer Position:": "晶圆位置:",
|
||||
"Credits": "致谢",
|
||||
"Developers": "开发者",
|
||||
"Contributors": "贡献者",
|
||||
"Testers": "测试者",
|
||||
"Special Thanks": "特别感谢",
|
||||
"Unknown": "未知",
|
||||
"Installed": "已安装",
|
||||
"Not Installed": "未安装",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "啤酒软件许可协议",
|
||||
"Default": "默认",
|
||||
"Do Not Override": "不修改",
|
||||
"Disabled": "已禁用",
|
||||
"Enabled": "已启用",
|
||||
" \\ue0e3 Reset": " \\ue0e3 重置",
|
||||
"Display": "显示",
|
||||
"Application changed\\n\\n": "应用已变更\\n\\n",
|
||||
"The running application changed\\n\\n": "正在运行的应用已变更\\n\\n",
|
||||
"while editing was going on.": "编辑过程中发生变更。",
|
||||
"Board": "主板",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"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": "致命错误",
|
||||
"Temporary Overrides ": "临时配置 ",
|
||||
"Sleep Mode": "睡眠模式",
|
||||
"Stock": "原厂默认",
|
||||
"Dev OC": "开发者超频",
|
||||
"Boost Mode": "加速模式",
|
||||
"Safe Max": "安全最大值",
|
||||
"Unsafe Max": "危险最大值",
|
||||
"Absolute Max": "绝对最大值",
|
||||
"Handheld Safe Max": "掌机模式安全最大值",
|
||||
"Enable": "启用",
|
||||
"Edit App Profile": "编辑应用配置",
|
||||
"Edit Global Profile": "编辑全局配置",
|
||||
"Temporary Overrides": "临时配置",
|
||||
"Settings": "设置",
|
||||
"About": "关于",
|
||||
"Compiling with minimal features": "以最小功能编译",
|
||||
"General Settings": "通用设置",
|
||||
"Governor Settings": "调频器设置",
|
||||
"Safety Settings": "安全设置",
|
||||
"Save KIP Settings": "保存 KIP 设置",
|
||||
"RAM Settings": "内存设置",
|
||||
"CPU Settings": "CPU 设置",
|
||||
"GPU Settings": "GPU 设置",
|
||||
"Display Settings": "显示设置",
|
||||
"Experimental": "实验性功能",
|
||||
"GPU Scheduling Override Method": "GPU 调度覆盖方式",
|
||||
"can be dangerous and may cause": "存在风险,可能导致",
|
||||
"damage to your battery or charger!": "电池或充电器损坏!",
|
||||
"Charge Current Override": "充电电流修改",
|
||||
"RAM Voltage Display Mode": "内存电压显示模式",
|
||||
"Polling Interval": "刷新间隔",
|
||||
"CPU Governor Minimum Frequency": "CPU 调频器最低频率",
|
||||
"\uE150 Usage of unsafe display": "\uE150 不安全的显示屏",
|
||||
"refresh rates may cause stress": "刷新率可能会对",
|
||||
"or damage to your display! ": "显示屏造成压力或损坏! ",
|
||||
"Proceed at your own risk!": "操作风险自负!",
|
||||
"Max Handheld Display": "掌机模式最大显示率",
|
||||
"Display Clock": "显示时钟",
|
||||
"Official Rating": "官方额定值",
|
||||
"TDP Threshold": "TDP 阈值",
|
||||
"Power": "电源",
|
||||
"Thermal Throttle Limit": "温控设置",
|
||||
"HP Mode": "高性能模式",
|
||||
"Default (Mariko)": "默认 (Mariko)",
|
||||
"Default (Erista)": "默认 (Erista)",
|
||||
"Rating": "额定值",
|
||||
"Safe Max (Mariko)": "安全最大值 (Mariko)",
|
||||
"Safe Max (Erista)": "安全最大值 (Erista)",
|
||||
"RAM VDD2 Voltage": "内存 VDD2 电压",
|
||||
"Voltage": "电压",
|
||||
"RAM VDDQ Voltage": "内存 VDDQ 电压",
|
||||
"RAM Frequency Editor": "内存频率编辑器",
|
||||
"JEDEC.": "JEDEC 标准。",
|
||||
"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)",
|
||||
"Ram Max Clock": "内存最大频率",
|
||||
"RAM Latency Editor": "内存延迟编辑器",
|
||||
"RAM Timing Reductions": "内存时序优化",
|
||||
"Memory Timings": "内存时序",
|
||||
"Memory": "内存",
|
||||
"mem": "内存",
|
||||
"Governor": "调频器",
|
||||
"Advanced": "高级",
|
||||
"Docked": "底座模式",
|
||||
"Handheld": "掌机模式",
|
||||
"Charging": "充电中",
|
||||
"USB Charger": "USB 充电器",
|
||||
"PD Charger": "PD 充电器",
|
||||
"Handheld TDP": "掌机模式功耗限制",
|
||||
"Thermal Throttle": "温度控制",
|
||||
"Uncapped Clocks": "解除频率上限",
|
||||
"Soc DVB Shift": "SoC DVB偏移",
|
||||
"Overwrite Boost Mode": "接管官方CPU调度",
|
||||
"Display Refresh Rate Changing": "显示刷新率变更",
|
||||
"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": "写入延迟",
|
||||
"CPU Boost Clock": "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 低压最低电压",
|
||||
"CPU High VMIN": "CPU 高压最低电压",
|
||||
"No Undervolt": "不降压",
|
||||
"SLT Table": "SLT 表",
|
||||
"HiOPT Table": "HiOPT 表",
|
||||
"GPU Undervolt Table": "GPU 降压表",
|
||||
"GPU Minimum Voltage": "GPU 最低电压",
|
||||
"Calculate GPU Vmin": "计算 GPU 最低电压",
|
||||
"GPU VMIN": "GPU 最低电压",
|
||||
"GPU Maximum Voltage": "GPU 最大电压",
|
||||
"GPU Voltage Offset": "GPU 电压偏移",
|
||||
"Do not override": "不修改",
|
||||
"Enabled (Default)": "已启用 (默认)",
|
||||
"96.6% limit": "96.6% 限制",
|
||||
"99.7% limit": "99.7% 限制",
|
||||
"GPU Scheduling Override": "GPU 调度修改",
|
||||
"Official Service": "官方服务",
|
||||
"GPU DVFS Mode": "GPU DVFS 模式",
|
||||
"GPU DVFS Offset": "GPU DVFS 偏移",
|
||||
"GPU Voltage Table": "GPU 电压表",
|
||||
"GPU Custom Table (mV)": "GPU 自定义表 (mV)",
|
||||
"\uE150 Setting GPU Clocks past": "\uE150 将 GPU 频率设置超过",
|
||||
"1075MHz without UV, 1152MHz on SLT": "1075MHz 无降压,SLT 表下 1152MHz",
|
||||
"or 1228MHz on HiOPT can cause ": "或 HiOPT 表下 1228MHz 可能导致 ",
|
||||
"permanent damage to your Switch!": "Switch 永久损坏!",
|
||||
"921MHz without UV and 960MHz on": "921MHz 无降压,SLT/HiOPT 表下 960MHz",
|
||||
"SLT or HiOPT can cause ": "可能导致 "
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "資訊",
|
||||
"IDDQ:": "國際電話號碼:",
|
||||
"Module: ": "模組:",
|
||||
"sys-dock status:": "系統塢站狀態:",
|
||||
"SaltyNX status:": "SaltyNX 狀態:",
|
||||
"RR Display status:": "RR 顯示狀態:",
|
||||
"Wafer Position:": "晶圓位置:",
|
||||
"Credits": "製作人員",
|
||||
"Developers": "開發商",
|
||||
"Contributors": "貢獻者",
|
||||
"Testers": "測試人員",
|
||||
"Special Thanks": "特別感謝",
|
||||
"Unknown": "未知",
|
||||
"Installed": "已安裝",
|
||||
"Not Installed": "未安裝",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "啤酒製品許可證",
|
||||
"Default": "預設",
|
||||
"Do Not Override": "不要覆蓋",
|
||||
"Disabled": "殘障人士",
|
||||
"Enabled": "啟用",
|
||||
" \\ue0e3 Reset": "\\ue0e3 重設",
|
||||
"Display": "顯示",
|
||||
"Application changed\\n\\n": "應用程式已更改\\n\\n",
|
||||
"The running application changed\\n\\n": "正在運行的應用程式已更改\\n\\n",
|
||||
"while editing was going on.": "當編輯正在進行時。",
|
||||
"Board": "董事會",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"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": "致命錯誤",
|
||||
"Temporary Overrides ": "臨時覆蓋",
|
||||
"Sleep Mode": "睡眠模式",
|
||||
"Stock": "庫存",
|
||||
"Dev OC": "開發OC",
|
||||
"Boost Mode": "升壓模式",
|
||||
"Safe Max": "安全最大值",
|
||||
"Unsafe Max": "不安全最大值",
|
||||
"Absolute Max": "絕對最大值",
|
||||
"Handheld Safe Max": "手持式安全最大",
|
||||
"Enable": "啟用",
|
||||
"Edit App Profile": "編輯應用程式設定檔",
|
||||
"Edit Global Profile": "編輯全域設定檔",
|
||||
"Temporary Overrides": "臨時覆蓋",
|
||||
"Settings": "設定",
|
||||
"About": "關於",
|
||||
"Compiling with minimal features": "使用最少的功能進行編譯",
|
||||
"General Settings": "常規設定",
|
||||
"Governor Settings": "調速器設定",
|
||||
"Safety Settings": "安全設定",
|
||||
"Save KIP Settings": "儲存 KIP 設定",
|
||||
"RAM Settings": "記憶體設定",
|
||||
"CPU Settings": "中央處理器設定",
|
||||
"GPU Settings": "GPU設定",
|
||||
"Display Settings": "顯示設定",
|
||||
"Experimental": "實驗性的",
|
||||
"GPU Scheduling Override Method": "GPU調度覆蓋方法",
|
||||
"can be dangerous and may cause": "可能很危險並可能導致",
|
||||
"damage to your battery or charger!": "損壞電池或充電器!",
|
||||
"Charge Current Override": "充電電流覆蓋",
|
||||
"RAM Voltage Display Mode": "RAM電壓顯示模式",
|
||||
"Polling Interval": "輪詢間隔",
|
||||
"CPU Governor Minimum Frequency": "CPU調速器最低頻率",
|
||||
"refresh rates may cause stress": "刷新率可能會造成壓力",
|
||||
"or damage to your display! ": "或損壞您的顯示器!",
|
||||
"Proceed at your own risk!": "請自行承擔風險!",
|
||||
"Max Handheld Display": "最大手持顯示器",
|
||||
"Display Clock": "顯示時鐘",
|
||||
"Official Rating": "官方評級",
|
||||
"TDP Threshold": "TDP閾值",
|
||||
"Power": "電源",
|
||||
"Thermal Throttle Limit": "熱油門限制",
|
||||
"HP Mode": "惠普模式",
|
||||
"Default (Mariko)": "預設(真理子)",
|
||||
"Default (Erista)": "預設(埃里斯塔)",
|
||||
"Rating": "評級",
|
||||
"Safe Max (Mariko)": "安全最大(真理子)",
|
||||
"Safe Max (Erista)": "安全最大(埃里斯塔)",
|
||||
"RAM VDD2 Voltage": "RAM VDD2 電壓",
|
||||
"Voltage": "電壓",
|
||||
"RAM VDDQ Voltage": "RAM VDDQ 電壓",
|
||||
"RAM Frequency Editor": "RAM頻率編輯器",
|
||||
"JEDEC.": "JEDEC。",
|
||||
"High speedo needed!": "需要高速!",
|
||||
"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)",
|
||||
"Ram Max Clock": "記憶體最大時鐘",
|
||||
"RAM Latency Editor": "RAM 延遲編輯器",
|
||||
"RAM Timing Reductions": "RAM 時序減少",
|
||||
"Memory Timings": "記憶體時序",
|
||||
"Advanced": "進階",
|
||||
"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": "寫入延遲",
|
||||
"CPU Boost Clock": "CPU 升壓時鐘",
|
||||
"CPU UV": "中央處理器紫外線",
|
||||
"CPU Unlock": "CPU解鎖",
|
||||
"CPU VMIN": "CPU最低電壓",
|
||||
"CPU Max Voltage": "CPU最大電壓",
|
||||
"CPU Max Clock": "CPU 最大時脈",
|
||||
"Extreme UV Table": "極端紫外線表",
|
||||
"CPU UV Table": "CPU UV表",
|
||||
"CPU Low UV": "CPU低紫外線",
|
||||
"CPU High UV": "CPU高紫外線",
|
||||
"CPU Low VMIN": "CPU 低 VMIN",
|
||||
"CPU High VMIN": "CPU 高 VMIN",
|
||||
"No Undervolt": "無欠壓",
|
||||
"SLT Table": "SLT表",
|
||||
"HiOPT Table": "HiOPT表",
|
||||
"GPU Undervolt Table": "GPU 欠壓表",
|
||||
"GPU Minimum Voltage": "GPU最低電壓",
|
||||
"Calculate GPU Vmin": "計算 GPU Vmin",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "GPU最大電壓",
|
||||
"GPU Voltage Offset": "GPU電壓偏移",
|
||||
"Do not override": "不要覆蓋",
|
||||
"Enabled (Default)": "啟用(預設)",
|
||||
"96.6% limit": "96.6%限制",
|
||||
"99.7% limit": "99.7%限制",
|
||||
"GPU Scheduling Override": "GPU 調度覆蓋",
|
||||
"Official Service": "官方服務",
|
||||
"GPU DVFS Mode": "GPU DVFS 模式",
|
||||
"GPU DVFS Offset": "GPU DVFS 偏移",
|
||||
"GPU Voltage Table": "GPU電壓表",
|
||||
"GPU Custom Table (mV)": "GPU 自訂表 (mV)",
|
||||
"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 可能會導致"
|
||||
}
|
||||
Submodule Source/hoc-clk/overlay/lib/libultrahand deleted from 8a1dbe9910
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
std::map<uint32_t, std::string> cpu_freq_label_m = {
|
||||
{612000000, "Sleep Mode"},
|
||||
{1020000000, "Stock"},
|
||||
{1224000000, "Dev OC"},
|
||||
{1785000000, "Boost Mode"},
|
||||
{1963000000, "Safe Max"},
|
||||
{2397000000, "Unsafe Max"},
|
||||
{2703000000, "Absolute Max"},
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::string> cpu_freq_label_m_uv = {
|
||||
{612000000, "Sleep Mode"},
|
||||
{1020000000, "Stock"},
|
||||
{1224000000, "Dev OC"},
|
||||
{1785000000, "Boost Mode"},
|
||||
{2397000000, "Safe Max"},
|
||||
{2499000000, "Unsafe Max"},
|
||||
{2703000000, "Absolute Max"},
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::string> cpu_freq_label_e = {
|
||||
{612000000, "Sleep Mode"},
|
||||
{1020000000, "Stock"},
|
||||
{1224000000, "Dev OC"},
|
||||
{1785000000, "Safe Max"},
|
||||
{2091000000, "Unsafe Max"},
|
||||
{2397000000, "Absolute Max"},
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::string> cpu_freq_label_e_uv = {
|
||||
{612000000, "Sleep Mode"},
|
||||
{1020000000, "Stock"},
|
||||
{1224000000, "Dev OC"},
|
||||
{1785000000, "Boost Mode"},
|
||||
{2091000000, "Safe Max"},
|
||||
{2193000000, "Unsafe Max"},
|
||||
{2397000000, "Absolute Max"},
|
||||
};
|
||||
|
||||
|
||||
std::map<uint32_t, std::string> gpu_freq_label_e = {
|
||||
{76800000, "Boost Mode"},
|
||||
{307200000, "Handheld"},
|
||||
{345600000, "Handheld"},
|
||||
{384000000, "Handheld"},
|
||||
{422400000, "Handheld"},
|
||||
{460800000, "Handheld Safe Max"},
|
||||
{768000000, "Docked"},
|
||||
{921600000, "Safe Max"},
|
||||
{960000000, "Unsafe Max"},
|
||||
{1075200000, "Absolute Max"},
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::string> gpu_freq_label_e_uv = {
|
||||
{76800000, "Boost Mode"},
|
||||
{307200000, "Handheld"},
|
||||
{345600000, "Handheld"},
|
||||
{384000000, "Handheld"},
|
||||
{422400000, "Handheld"},
|
||||
{460800000, "Handheld Safe Max"},
|
||||
{768000000, "Docked"},
|
||||
{960000000, "Safe Max"},
|
||||
{1075200000, "Absolute Max"},
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::string> gpu_freq_label_m = {
|
||||
{76800000, "Boost Mode"},
|
||||
{307200000, "Handheld"},
|
||||
{384000000, "Handheld"},
|
||||
{460800000, "Handheld"},
|
||||
{614400000, "Handheld Safe Max"},
|
||||
{768000000, "Docked"},
|
||||
{1075200000, "Safe Max"},
|
||||
{1305600000, "Unsafe Max"},
|
||||
{1536000000, "Absolute Max"},
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::string> gpu_freq_label_m_slt = {
|
||||
{76800000, "Boost Mode"},
|
||||
{307200000, "Handheld"},
|
||||
{384000000, "Handheld"},
|
||||
{460800000, "Handheld"},
|
||||
{614400000, "Handheld Safe Max"},
|
||||
{768000000, "Docked"},
|
||||
{1152200000, "Safe Max"},
|
||||
{1305600000, "Unsafe Max"},
|
||||
{1536000000, "Absolute Max"},
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::string> gpu_freq_label_m_hiopt = {
|
||||
{76800000, "Boost Mode"},
|
||||
{307200000, "Handheld"},
|
||||
{384000000, "Handheld"},
|
||||
{460800000, "Handheld"},
|
||||
{614400000, "Handheld Safe Max"},
|
||||
{768000000, "Docked"},
|
||||
{1228800000, "Safe Max"},
|
||||
{1305600000, "Unsafe Max"},
|
||||
{1536000000, "Absolute Max"},
|
||||
};
|
||||
|
||||
std::map<uint32_t, std::string>* marikoUV[3] {
|
||||
&gpu_freq_label_m,
|
||||
&gpu_freq_label_m_slt,
|
||||
&gpu_freq_label_m_hiopt,
|
||||
};
|
||||
|
||||
|
||||
std::map<uint32_t, std::string>* eristaUV[3] {
|
||||
&gpu_freq_label_e,
|
||||
&gpu_freq_label_e_uv,
|
||||
&gpu_freq_label_e_uv,
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
extern std::map<uint32_t, std::string> cpu_freq_label_m;
|
||||
extern std::map<uint32_t, std::string> cpu_freq_label_m_uv;
|
||||
extern std::map<uint32_t, std::string> cpu_freq_label_e;
|
||||
extern std::map<uint32_t, std::string> cpu_freq_label_e_uv;
|
||||
extern std::map<uint32_t, std::string> gpu_freq_label_m;
|
||||
extern std::map<uint32_t, std::string> gpu_freq_label_m_slt;
|
||||
extern std::map<uint32_t, std::string> gpu_freq_label_m_hiopt;
|
||||
extern std::map<uint32_t, std::string> gpu_freq_label_e;
|
||||
extern std::map<uint32_t, std::string> gpu_freq_label_e_uv;
|
||||
|
||||
extern std::map<uint32_t, std::string>* marikoUV[3];
|
||||
extern std::map<uint32_t, std::string>* eristaUV[3];
|
||||
@@ -1,237 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <nxExt.h>
|
||||
#include <hocclk.h>
|
||||
#include <switch.h>
|
||||
#include <pwm.h>
|
||||
#include <registers.h>
|
||||
#include <battery.h>
|
||||
#include "display_refresh_rate.hpp"
|
||||
#include <rgltr.h>
|
||||
#include <notification.h>
|
||||
|
||||
#include "board.hpp"
|
||||
#include "board_fuse.hpp"
|
||||
#include "board_load.hpp"
|
||||
#include "board_volt.hpp"
|
||||
#include "board_misc.hpp"
|
||||
#include "../soctherm.hpp"
|
||||
#include "../integrations.hpp"
|
||||
#include "../file_utils.hpp"
|
||||
namespace board {
|
||||
|
||||
HocClkSocType gSocType;
|
||||
u8 gDramID;
|
||||
HocClkConsoleType gConsoleType = HocClkConsoleType_Iowa;
|
||||
FuseData fuseData;
|
||||
u8 speedoBracket;
|
||||
PwmChannelSession iCon;
|
||||
|
||||
u32 fd = 0, fd2 = 0;
|
||||
|
||||
void FetchHardwareInfos() {
|
||||
ReadFuses(fuseData);
|
||||
SetGpuBracket(fuseData.gpuSpeedo, speedoBracket);
|
||||
|
||||
u64 sku = 0, dramID = 0;
|
||||
Result rc = splInitialize();
|
||||
ASSERT_RESULT_OK(rc, "splInitialize");
|
||||
|
||||
rc = splGetConfig(SplConfigItem_HardwareType, &sku);
|
||||
ASSERT_RESULT_OK(rc, "splGetConfig");
|
||||
|
||||
rc = splGetConfig(SplConfigItem_DramId, &dramID);
|
||||
ASSERT_RESULT_OK(rc, "splGetConfig");
|
||||
gDramID = dramID;
|
||||
splExit();
|
||||
|
||||
switch(sku) {
|
||||
case 2 ... 5:
|
||||
gSocType = HocClkSocType_Mariko;
|
||||
break;
|
||||
default:
|
||||
gSocType = HocClkSocType_Erista;
|
||||
}
|
||||
|
||||
if (gSocType == HocClkSocType_Mariko) {
|
||||
CacheGpuVoltTable();
|
||||
}
|
||||
|
||||
gConsoleType = static_cast<HocClkConsoleType>(sku);
|
||||
}
|
||||
|
||||
/* TODO: Check for config */
|
||||
void Initialize() {
|
||||
Result rc = 0;
|
||||
|
||||
if (HOSSVC_HAS_CLKRST) {
|
||||
rc = clkrstInitialize();
|
||||
ASSERT_RESULT_OK(rc, "clkrstInitialize");
|
||||
} else {
|
||||
rc = pcvInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pcvInitialize");
|
||||
}
|
||||
|
||||
rc = apmExtInitialize();
|
||||
ASSERT_RESULT_OK(rc, "apmExtInitialize");
|
||||
|
||||
rc = psmInitialize();
|
||||
ASSERT_RESULT_OK(rc, "psmInitialize");
|
||||
|
||||
if(HOSSVC_HAS_TC) {
|
||||
rc = tcInitialize();
|
||||
ASSERT_RESULT_OK(rc, "tcInitialize");
|
||||
}
|
||||
|
||||
rc = max17050Initialize();
|
||||
ASSERT_RESULT_OK(rc, "max17050Initialize");
|
||||
|
||||
rc = tmp451Initialize();
|
||||
ASSERT_RESULT_OK(rc, "tmp451Initialize");
|
||||
|
||||
Result nvCheck = 1;
|
||||
if (R_SUCCEEDED(nvInitialize())) {
|
||||
nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu");
|
||||
Result nvCheck_sched = nvOpen(&fd2, "/dev/nvsched-ctrl");
|
||||
/* This can be improved. */
|
||||
NvSchedSucceed(nvCheck_sched);
|
||||
|
||||
if (R_SUCCEEDED(nvCheck_sched)) {
|
||||
SchedSetFD2(fd2);
|
||||
}
|
||||
}
|
||||
|
||||
rc = rgltrInitialize();
|
||||
ASSERT_RESULT_OK(rc, "rgltrInitialize");
|
||||
|
||||
rc = pmdmntInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pmdmntInitialize");
|
||||
|
||||
StartLoad(nvCheck, fd);
|
||||
|
||||
batteryInfoInitialize();
|
||||
FetchHardwareInfos();
|
||||
|
||||
soctherm::Initialize();
|
||||
|
||||
Result pwmCheck = 1;
|
||||
if (hosversionAtLeast(6,0,0) && R_SUCCEEDED(pwmInitialize())) {
|
||||
pwmCheck = pwmOpenSession2(&iCon, 0x3D000001);
|
||||
}
|
||||
|
||||
StartMiscThread(pwmCheck, &iCon);
|
||||
|
||||
u64 clkVirtAddr, dsiVirtAddr;
|
||||
|
||||
rc = QueryMemoryMapping(&clkVirtAddr, 0x60006000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (clk)");
|
||||
|
||||
rc = QueryMemoryMapping(&dsiVirtAddr, 0x54300000, 0x40000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (dsi)");
|
||||
|
||||
display::DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr, .isLite = (GetConsoleType() == HocClkConsoleType_Hoag), .isRetroSUPER = integrations::GetRETROSuperStatus()};
|
||||
display::Initialize(&cfg);
|
||||
|
||||
CacheDfllData();
|
||||
}
|
||||
|
||||
void Exit() {
|
||||
if (HOSSVC_HAS_CLKRST) {
|
||||
clkrstExit();
|
||||
} else {
|
||||
pcvExit();
|
||||
}
|
||||
|
||||
apmExtExit();
|
||||
psmExit();
|
||||
|
||||
if (HOSSVC_HAS_TC) {
|
||||
tcExit();
|
||||
}
|
||||
|
||||
max17050Exit();
|
||||
tmp451Exit();
|
||||
|
||||
ExitLoad();
|
||||
|
||||
ExitMiscThread();
|
||||
|
||||
pwmChannelSessionClose(&iCon);
|
||||
pwmExit();
|
||||
rgltrExit();
|
||||
batteryInfoExit();
|
||||
pmdmntExit();
|
||||
display::Shutdown();
|
||||
nvExit();
|
||||
}
|
||||
|
||||
HocClkSocType GetSocType() {
|
||||
return gSocType;
|
||||
}
|
||||
|
||||
HocClkConsoleType GetConsoleType() {
|
||||
return gConsoleType;
|
||||
}
|
||||
|
||||
u8 GetDramID() {
|
||||
return gDramID;
|
||||
}
|
||||
|
||||
bool IsDram8GB() {
|
||||
SecmonArgs args = {};
|
||||
args.X[0] = 0xF0000002;
|
||||
args.X[1] = MC_REGISTER_BASE + MC_EMEM_CFG_0;
|
||||
svcCallSecureMonitor(&args);
|
||||
|
||||
if (args.X[1] == (MC_REGISTER_BASE + MC_EMEM_CFG_0)) { // if param 1 is identical read failed
|
||||
notification::writeNotification("Horizon OC\nSecmon read failed!\n This may be a hardware issue!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return args.X[1] == 0x00002000 ? true : false;
|
||||
}
|
||||
|
||||
/* TODO: Put this into a different file. */
|
||||
void SetDisplayRefreshDockedState(bool docked) {
|
||||
if (GetConsoleType() != HocClkConsoleType_Hoag) {
|
||||
display::SetDockedState(docked);
|
||||
}
|
||||
}
|
||||
|
||||
FuseData *GetFuseData() {
|
||||
return &fuseData;
|
||||
}
|
||||
|
||||
u8 GetGpuSpeedoBracket() {
|
||||
return speedoBracket;
|
||||
}
|
||||
|
||||
bool IsUsingRetroSuperDisplay() {
|
||||
return false; /* stub for now. */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include "board_fuse.hpp"
|
||||
#include "board_load.hpp"
|
||||
#include "board_name.hpp"
|
||||
#include "board_freq.hpp"
|
||||
#include "board_sensor.hpp"
|
||||
#include "board_volt.hpp"
|
||||
#include "board_profile.hpp"
|
||||
#include "../mem_map.hpp"
|
||||
#define HOSSVC_HAS_CLKRST (hosversionAtLeast(8,0,0))
|
||||
#define HOSSVC_HAS_TC (hosversionAtLeast(5,0,0))
|
||||
|
||||
namespace board {
|
||||
|
||||
void Initialize();
|
||||
void Exit();
|
||||
HocClkSocType GetSocType();
|
||||
HocClkConsoleType GetConsoleType();
|
||||
u8 GetDramID();
|
||||
u8 GetGpuSpeedoBracket();
|
||||
bool IsDram8GB();
|
||||
void SetDisplayRefreshDockedState(bool docked);
|
||||
FuseData *GetFuseData();
|
||||
bool IsUsingRetroSuperDisplay();
|
||||
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include <nxExt.h>
|
||||
#include "display_refresh_rate.hpp"
|
||||
#include "board.hpp"
|
||||
#include "board_name.hpp"
|
||||
#include "../errors.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
PcvModule GetPcvModule(HocClkModule hocclkModule) {
|
||||
switch (hocclkModule) {
|
||||
case HocClkModule_CPU:
|
||||
return PcvModule_CpuBus;
|
||||
case HocClkModule_GPU:
|
||||
return PcvModule_GPU;
|
||||
case HocClkModule_MEM:
|
||||
return PcvModule_EMC;
|
||||
default:
|
||||
ASSERT_ENUM_VALID(HocClkModule, hocclkModule);
|
||||
}
|
||||
|
||||
return static_cast<PcvModule>(0);
|
||||
}
|
||||
|
||||
PcvModuleId GetPcvModuleId(HocClkModule hocclkModule) {
|
||||
PcvModuleId pcvModuleId;
|
||||
Result rc = pcvGetModuleId(&pcvModuleId, GetPcvModule(hocclkModule));
|
||||
ASSERT_RESULT_OK(rc, "pcvGetModuleId");
|
||||
|
||||
return pcvModuleId;
|
||||
}
|
||||
|
||||
void ClkrstSetHz(ClkrstSession &session, u32 hz) {
|
||||
ASSERT_RESULT_OK(clkrstSetClockRate(&session, hz), "clkrstSetClockRate");
|
||||
}
|
||||
|
||||
void PcvSetHz(PcvModule moduleID, u32 hz) {
|
||||
ASSERT_RESULT_OK(pcvSetClockRate(moduleID, hz), "pcvSetClockRate");
|
||||
}
|
||||
|
||||
void SetHz(HocClkModule module, u32 hz) {
|
||||
Result rc = 0;
|
||||
bool usesGovenor = module > HocClkModule_MEM;
|
||||
|
||||
|
||||
if (module == HocClkModule_Display) {
|
||||
display::SetRate(hz);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usesGovenor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (HOSSVC_HAS_CLKRST) {
|
||||
ClkrstSession session = {};
|
||||
rc = clkrstOpenSession(&session, GetPcvModuleId(module), 3);
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
ClkrstSetHz(session, hz);
|
||||
|
||||
/* Voltage bug workaround. */
|
||||
if (module == HocClkModule_CPU) {
|
||||
svcSleepThread(250'000);
|
||||
ClkrstSetHz(session, hz);
|
||||
}
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
} else {
|
||||
PcvSetHz(GetPcvModule(module), hz);
|
||||
|
||||
if (module == HocClkModule_CPU) {
|
||||
svcSleepThread(250'000);
|
||||
PcvSetHz(GetPcvModule(module), hz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetDisplayRate(u32 hz) {
|
||||
display::GetRate(&hz, false);
|
||||
return hz;
|
||||
}
|
||||
|
||||
u32 GetHz(HocClkModule module) {
|
||||
Result rc = 0;
|
||||
u32 hz = 0;
|
||||
|
||||
if (module == HocClkModule_Display) {
|
||||
return GetDisplayRate(hz);
|
||||
}
|
||||
|
||||
if (HOSSVC_HAS_CLKRST) {
|
||||
ClkrstSession session = {};
|
||||
|
||||
rc = clkrstOpenSession(&session, GetPcvModuleId(module), 3);
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
|
||||
rc = clkrstGetClockRate(&session, &hz);
|
||||
ASSERT_RESULT_OK(rc, "clkrstGetClockRate");
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
} else {
|
||||
rc = pcvGetClockRate(GetPcvModule(module), &hz);
|
||||
ASSERT_RESULT_OK(rc, "pcvGetClockRate");
|
||||
}
|
||||
|
||||
return hz;
|
||||
}
|
||||
|
||||
u32 GetRealHz(HocClkModule module) {
|
||||
u32 hz = 0;
|
||||
switch (module) {
|
||||
case HocClkModule_CPU:
|
||||
return t210ClkCpuFreq();
|
||||
case HocClkModule_GPU:
|
||||
return t210ClkGpuFreq();
|
||||
case HocClkModule_MEM:
|
||||
return t210ClkMemFreq();
|
||||
case HocClkModule_Display:
|
||||
return GetDisplayRate(hz);
|
||||
return hz;
|
||||
default:
|
||||
ASSERT_ENUM_VALID(HocClkModule, module);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GetFreqList(HocClkModule module, u32 *outList, u32 maxCount, u32 *outCount) {
|
||||
Result rc = 0;
|
||||
PcvClockRatesListType type;
|
||||
s32 tmpInMaxCount = maxCount;
|
||||
s32 tmpOutCount = 0;
|
||||
|
||||
|
||||
if (HOSSVC_HAS_CLKRST) {
|
||||
ClkrstSession session = {};
|
||||
|
||||
rc = clkrstOpenSession(&session, GetPcvModuleId(module), 3);
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
|
||||
rc = clkrstGetPossibleClockRates(&session, outList, tmpInMaxCount, &type, &tmpOutCount);
|
||||
ASSERT_RESULT_OK(rc, "clkrstGetPossibleClockRates");
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
} else {
|
||||
rc = pcvGetPossibleClockRates(GetPcvModule(module), outList, tmpInMaxCount, &type, &tmpOutCount);
|
||||
ASSERT_RESULT_OK(rc, "pcvGetPossibleClockRates");
|
||||
}
|
||||
|
||||
if (type != PcvClockRatesListType_Discrete) {
|
||||
ERROR_THROW("Unexpected PcvClockRatesListType: %u (module = %s)", type, GetModuleName(module, false));
|
||||
}
|
||||
|
||||
*outCount = tmpOutCount;
|
||||
}
|
||||
|
||||
u32 GetHighestDockedDisplayRate() {
|
||||
if (GetConsoleType() != HocClkConsoleType_Hoag) {
|
||||
return display::GetDockedHighestAllowed();
|
||||
}
|
||||
|
||||
return 60;
|
||||
}
|
||||
|
||||
void ResetToStock() {
|
||||
Result rc;
|
||||
if (hosversionAtLeast(9,0,0)) {
|
||||
std::uint32_t confId = 0;
|
||||
rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
|
||||
HocClkApmConfiguration* apmConfiguration = nullptr;
|
||||
for (size_t i = 0; hocclk_g_apm_configurations[i].id; ++i) {
|
||||
if(hocclk_g_apm_configurations[i].id == confId) {
|
||||
apmConfiguration = &hocclk_g_apm_configurations[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!apmConfiguration) {
|
||||
ERROR_THROW("Unknown apm configuration: %x", confId);
|
||||
}
|
||||
|
||||
SetHz(HocClkModule_CPU, apmConfiguration->cpu_hz);
|
||||
SetHz(HocClkModule_GPU, apmConfiguration->gpu_hz);
|
||||
SetHz(HocClkModule_MEM, apmConfiguration->mem_hz);
|
||||
} else {
|
||||
u32 mode = 0;
|
||||
rc = apmExtGetPerformanceMode(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
|
||||
|
||||
rc = apmExtSysRequestPerformanceMode(mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode");
|
||||
}
|
||||
}
|
||||
|
||||
void ResetToStockDisplay() {
|
||||
display::SetRate(60);
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include <nxExt.h>
|
||||
#include "../errors.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
void SetHz(HocClkModule module, u32 hz);
|
||||
|
||||
u32 GetHz(HocClkModule module);
|
||||
u32 GetRealHz(HocClkModule module);
|
||||
void GetFreqList(HocClkModule module, u32 *outList, u32 maxCount, u32 *outCount);
|
||||
u32 GetHighestDockedDisplayRate();
|
||||
|
||||
void ResetToStock();
|
||||
void ResetToStockDisplay();
|
||||
|
||||
template <typename Getter>
|
||||
void ResetToStockModule(Getter getHzFunc, HocClkModule module) {
|
||||
Result rc = 0;
|
||||
|
||||
if (hosversionAtLeast(9, 0, 0)) {
|
||||
u32 confId = 0;
|
||||
rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
|
||||
HocClkApmConfiguration* apmConfiguration = nullptr;
|
||||
for (size_t i = 0; hocclk_g_apm_configurations[i].id; ++i) {
|
||||
|
||||
if (hocclk_g_apm_configurations[i].id == confId) {
|
||||
apmConfiguration = &hocclk_g_apm_configurations[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!apmConfiguration) {
|
||||
ERROR_THROW("Unknown apm configuration: %x", confId);
|
||||
}
|
||||
|
||||
SetHz(module, getHzFunc(*apmConfiguration));
|
||||
} else {
|
||||
u32 mode = 0;
|
||||
rc = apmExtGetPerformanceMode(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
|
||||
|
||||
rc = apmExtSysRequestPerformanceMode(mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode");
|
||||
}
|
||||
}
|
||||
|
||||
inline void ResetToStockCpu() {
|
||||
ResetToStockModule([](const HocClkApmConfiguration& cfg) {return cfg.cpu_hz; }, HocClkModule_CPU);
|
||||
}
|
||||
|
||||
inline void ResetToStockGpu() {
|
||||
ResetToStockModule([](const HocClkApmConfiguration& cfg){ return cfg.gpu_hz; }, HocClkModule_GPU);
|
||||
}
|
||||
|
||||
inline void ResetToStockMem() {
|
||||
ResetToStockModule([](const HocClkApmConfiguration& cfg){ return cfg.mem_hz; }, HocClkModule_MEM);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <fuse.h>
|
||||
#include "board_fuse.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace board {
|
||||
|
||||
void SetGpuBracket(u16 speedo, u8 &gpuBracket) {
|
||||
if (speedo <= 1624) {
|
||||
gpuBracket = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (speedo <= 1689) {
|
||||
gpuBracket = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (speedo <= 1753) {
|
||||
gpuBracket = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
/* >= 1754 */
|
||||
gpuBracket = 3;
|
||||
}
|
||||
|
||||
void ReadFuses(FuseData &speedo) {
|
||||
u64 pid = 0;
|
||||
constexpr u64 UsbID = 0x0100000000000006;
|
||||
if (R_FAILED(pmdmntGetProcessId(&pid, UsbID))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Handle debug;
|
||||
if (R_FAILED(svcDebugActiveProcess(&debug, pid))) {
|
||||
return;
|
||||
}
|
||||
|
||||
MemoryInfo mem_info = {};
|
||||
u32 pageinfo = 0;
|
||||
u64 addr = 0;
|
||||
|
||||
u8 stack[0x10] = {};
|
||||
const u8 compare[0x10] = {};
|
||||
u8 dump[0x400] = {};
|
||||
constexpr u64 PageSize = 0x1000;
|
||||
|
||||
while (true) {
|
||||
if (R_FAILED(svcQueryDebugProcessMemory(&mem_info, &pageinfo, debug, addr)) || mem_info.addr < addr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mem_info.type == MemType_Io && mem_info.size == PageSize) {
|
||||
if (R_FAILED(svcReadDebugProcessMemory(stack, debug, mem_info.addr, sizeof(stack)))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(stack, compare, sizeof(stack)) == 0) {
|
||||
if (R_FAILED(svcReadDebugProcessMemory(dump, debug, mem_info.addr + 0x800, sizeof(dump)))) {
|
||||
break;
|
||||
}
|
||||
|
||||
speedo.cpuSpeedo = *reinterpret_cast<u16*>(dump + FUSE_CPU_SPEEDO_0_CALIB);
|
||||
speedo.gpuSpeedo = *reinterpret_cast<u16*>(dump + FUSE_CPU_SPEEDO_2_CALIB);
|
||||
speedo.socSpeedo = *reinterpret_cast<u16*>(dump + FUSE_SOC_SPEEDO_0_CALIB);
|
||||
|
||||
speedo.cpuIDDQ = *reinterpret_cast<u16*>(dump + FUSE_CPU_IDDQ_CALIB) * 4;
|
||||
speedo.gpuIDDQ = *reinterpret_cast<u16*>(dump + FUSE_GPU_IDDQ_CALIB) * 5;
|
||||
speedo.socIDDQ = *reinterpret_cast<u16*>(dump + FUSE_SOC_IDDQ_CALIB) * 4;
|
||||
speedo.waferX = *reinterpret_cast<u16*>(dump + FUSE_OPT_X_COORDINATE);
|
||||
speedo.waferY = *reinterpret_cast<u16*>(dump + FUSE_OPT_Y_COORDINATE);
|
||||
|
||||
svcCloseHandle(debug);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
addr = mem_info.addr + mem_info.size;
|
||||
}
|
||||
|
||||
svcCloseHandle(debug);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include <nxExt.h>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
#include <numeric>
|
||||
#include <minIni.h>
|
||||
#include <battery.h>
|
||||
#include "board_misc.hpp"
|
||||
#include "board.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
Thread gpuThread;
|
||||
LEvent threadexit;
|
||||
Thread cpuCore0Thread;
|
||||
Thread cpuCore1Thread;
|
||||
Thread cpuCore2Thread;
|
||||
|
||||
u32 gpuLoad;
|
||||
u32 _fd, _fd2;
|
||||
Result nvCheckSched = 0;
|
||||
Result nvCheck_load = 0;
|
||||
u64 idletick0 = 0;
|
||||
u64 idletick1 = 0;
|
||||
u64 idletick2 = 0;
|
||||
|
||||
constexpr u64 CpuTimeOutNs = 500'000'000;
|
||||
constexpr double Systemtickfrequency = 19200000.0 * (static_cast<double>(CpuTimeOutNs) / 1'000'000'000.0);
|
||||
|
||||
void GpuLoadThread(void *ptr) {
|
||||
#define gpu_samples_average 8
|
||||
#define NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD 0x80044715
|
||||
uint32_t gpu_load_array[gpu_samples_average] = {0};
|
||||
size_t i = 0;
|
||||
if (R_SUCCEEDED(nvCheck_load)) do {
|
||||
u32 temp;
|
||||
if (R_SUCCEEDED(nvIoctl(_fd, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &temp))) {
|
||||
gpu_load_array[i++ % gpu_samples_average] = temp;
|
||||
gpuLoad = std::accumulate(&gpu_load_array[0], &gpu_load_array[gpu_samples_average], 0) / gpu_samples_average;
|
||||
}
|
||||
svcSleepThread(16'666'000); // wait a bit (this is the perfect amount of time to keep the reading accurate)
|
||||
} while(true);
|
||||
}
|
||||
|
||||
void CheckCore(void *idletickPtr) {
|
||||
u64* idletick = static_cast<u64 *>(idletickPtr);
|
||||
while(true) {
|
||||
u64 idletickA;
|
||||
u64 idletickB;
|
||||
svcGetInfo(&idletickB, InfoType_IdleTickCount, INVALID_HANDLE, -1);
|
||||
svcWaitForAddress(&threadexit, ArbitrationType_WaitIfEqual, 0, CpuTimeOutNs);
|
||||
svcGetInfo(&idletickA, InfoType_IdleTickCount, INVALID_HANDLE, -1);
|
||||
*idletick = idletickA - idletickB;
|
||||
}
|
||||
}
|
||||
|
||||
void StartLoad(Result nvCheck, u32 fd) {
|
||||
_fd = fd;
|
||||
nvCheck_load = nvCheck;
|
||||
threadCreate(&gpuThread, GpuLoadThread, &nvCheck, NULL, 0x1000, 0x3F, -2);
|
||||
threadStart(&gpuThread);
|
||||
|
||||
leventClear(&threadexit);
|
||||
threadCreate(&cpuCore0Thread, CheckCore, &idletick0, NULL, 0x1000, 0x10, 0);
|
||||
threadCreate(&cpuCore1Thread, CheckCore, &idletick1, NULL, 0x1000, 0x10, 1);
|
||||
threadCreate(&cpuCore2Thread, CheckCore, &idletick2, NULL, 0x1000, 0x10, 2);
|
||||
|
||||
threadStart(&cpuCore0Thread);
|
||||
threadStart(&cpuCore1Thread);
|
||||
threadStart(&cpuCore2Thread);
|
||||
}
|
||||
|
||||
u32 GetMaxCpuLoad() {
|
||||
float cpuUsage0 = std::clamp(((Systemtickfrequency - idletick0) / static_cast<double>(Systemtickfrequency)) * 1000.0, 0.0, 1000.0);
|
||||
float cpuUsage1 = std::clamp(((Systemtickfrequency - idletick1) / static_cast<double>(Systemtickfrequency)) * 1000.0, 0.0, 1000.0);
|
||||
float cpuUsage2 = std::clamp(((Systemtickfrequency - idletick2) / static_cast<double>(Systemtickfrequency)) * 1000.0, 0.0, 1000.0);
|
||||
return std::round(std::max({cpuUsage0, cpuUsage1, cpuUsage2}));
|
||||
}
|
||||
|
||||
u32 GetPartLoad(HocClkPartLoad loadSource) {
|
||||
switch(loadSource) {
|
||||
case HocClkPartLoad_EMC:
|
||||
return t210EmcLoadAll();
|
||||
case HocClkPartLoad_EMCCpu:
|
||||
return t210EmcLoadCpu();
|
||||
case HocClkPartLoad_GPU:
|
||||
return gpuLoad;
|
||||
case HocClkPartLoad_CPUMax:
|
||||
return GetMaxCpuLoad();
|
||||
case HocClkPartLoad_BAT:
|
||||
BatteryChargeInfo info;
|
||||
batteryInfoGetChargeInfo(&info);
|
||||
return info.RawBatteryCharge;
|
||||
case HocClkPartLoad_FAN:
|
||||
return GetFanLevel();
|
||||
default:
|
||||
ASSERT_ENUM_VALID(HocClkPartLoad, loadSource);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExitLoad() {
|
||||
threadClose(&gpuThread);
|
||||
threadClose(&cpuCore0Thread);
|
||||
threadClose(&cpuCore1Thread);
|
||||
threadClose(&cpuCore2Thread);
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr u32 NVschedCtrlEnable = 0x00000601;
|
||||
constexpr u32 NVschedCtrlDisable = 0x00000602;
|
||||
}
|
||||
|
||||
void SetGpuSchedulingMode(GpuSchedulingMode mode, GpuSchedulingOverrideMethod method) {
|
||||
if (R_FAILED(nvCheckSched) && method == GpuSchedulingOverrideMethod_NvService) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32 temp;
|
||||
bool enabled = false;
|
||||
switch (mode) {
|
||||
case GpuSchedulingMode_DoNotOverride: break;
|
||||
case GpuSchedulingMode_Disabled:
|
||||
if (method == GpuSchedulingOverrideMethod_NvService) {
|
||||
nvIoctl(_fd2, NVschedCtrlDisable, &temp);
|
||||
} else {
|
||||
enabled = false;
|
||||
}
|
||||
break;
|
||||
case GpuSchedulingMode_Enabled:
|
||||
if (method == GpuSchedulingOverrideMethod_NvService) {
|
||||
nvIoctl(_fd2, NVschedCtrlEnable, &temp);
|
||||
} else {
|
||||
enabled = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASSERT_ENUM_VALID(GpuSchedulingMode, mode);
|
||||
}
|
||||
|
||||
if (method == GpuSchedulingOverrideMethod_Ini) {
|
||||
constexpr const char *IniPath = "sdmc:/atmosphere/config/system_settings.ini";
|
||||
constexpr const char *Section = "am.gpu";
|
||||
constexpr const char *Key = "gpu_scheduling_enabled";
|
||||
|
||||
const char *value = enabled ? "u8!0x1" : "u8!0x0";
|
||||
|
||||
ini_puts(Section, Key, value, IniPath);
|
||||
}
|
||||
}
|
||||
|
||||
void SchedSetFD2(u32 fd2) {
|
||||
_fd2 = fd2;
|
||||
}
|
||||
|
||||
void NvSchedSucceed(Result nvSched) {
|
||||
nvCheckSched = nvSched;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
|
||||
namespace board {
|
||||
|
||||
void StartLoad(Result nvCheck, u32 fd);
|
||||
void ExitLoad();
|
||||
u32 GetPartLoad(HocClkPartLoad loadSource);
|
||||
void SetGpuSchedulingMode(GpuSchedulingMode mode, GpuSchedulingOverrideMethod method);
|
||||
void SchedSetFD2(u32 fd2);
|
||||
void NvSchedSucceed(Result nvSched);
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include <pwm.h>
|
||||
|
||||
namespace board {
|
||||
|
||||
void StartMiscThread(Result pwmCheck, PwmChannelSession *iCon);
|
||||
void ExitMiscThread();
|
||||
u8 GetFanLevel();
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include "board.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
const char *GetModuleName(HocClkModule module, bool pretty) {
|
||||
ASSERT_ENUM_VALID(HocClkModule, module);
|
||||
return hocclkFormatModule(module, pretty);
|
||||
}
|
||||
|
||||
const char *GetProfileName(HocClkProfile profile, bool pretty) {
|
||||
ASSERT_ENUM_VALID(HocClkProfile, profile);
|
||||
return hocclkFormatProfile(profile, pretty);
|
||||
}
|
||||
|
||||
const char *GetThermalSensorName(HocClkThermalSensor sensor, bool pretty) {
|
||||
ASSERT_ENUM_VALID(HocClkThermalSensor, sensor);
|
||||
return hocclkFormatThermalSensor(sensor, pretty);
|
||||
}
|
||||
|
||||
const char *GetPowerSensorName(HocClkPowerSensor sensor, bool pretty) {
|
||||
ASSERT_ENUM_VALID(HocClkPowerSensor, sensor);
|
||||
return hocclkFormatPowerSensor(sensor, pretty);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
|
||||
namespace board {
|
||||
|
||||
const char *GetModuleName(HocClkModule module, bool pretty);
|
||||
const char *GetProfileName(HocClkProfile profile, bool pretty);
|
||||
const char *GetThermalSensorName(HocClkThermalSensor sensor, bool pretty);
|
||||
const char *GetPowerSensorName(HocClkPowerSensor sensor, bool pretty);
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include <nxExt.h>
|
||||
#include "board.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
HocClkProfile GetProfile() {
|
||||
u32 mode = 0;
|
||||
Result rc = apmExtGetPerformanceMode(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
|
||||
|
||||
if (mode) {
|
||||
return HocClkProfile_Docked;
|
||||
}
|
||||
|
||||
PsmChargerType chargerType;
|
||||
|
||||
rc = psmGetChargerType(&chargerType);
|
||||
ASSERT_RESULT_OK(rc, "psmGetChargerType");
|
||||
|
||||
if (chargerType == PsmChargerType_EnoughPower) {
|
||||
return HocClkProfile_HandheldChargingOfficial;
|
||||
} else if (chargerType == PsmChargerType_LowPower) {
|
||||
return HocClkProfile_HandheldChargingUSB;
|
||||
}
|
||||
|
||||
return HocClkProfile_Handheld;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <hocclk.h>
|
||||
|
||||
namespace board {
|
||||
|
||||
HocClkProfile GetProfile();
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <hocclk.h>
|
||||
#include <switch.h>
|
||||
#include <nxExt.h>
|
||||
#include <cmath>
|
||||
#include <battery.h>
|
||||
#include <pwm.h>
|
||||
#include "board.hpp"
|
||||
#include "../soctherm.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
s32 GetTemperatureMilli(HocClkThermalSensor sensor) {
|
||||
s32 millis = 0;
|
||||
BatteryChargeInfo info;
|
||||
|
||||
soctherm::TSensorTemps temps = {};
|
||||
soctherm::ReadSensors(temps);
|
||||
|
||||
switch(sensor) {
|
||||
case HocClkThermalSensor_SOC: {
|
||||
millis = tmp451TempSoc();
|
||||
break;
|
||||
}
|
||||
case HocClkThermalSensor_PCB: {
|
||||
millis = tmp451TempPcb();
|
||||
break;
|
||||
}
|
||||
case HocClkThermalSensor_Skin: {
|
||||
if (HOSSVC_HAS_TC) {
|
||||
Result rc;
|
||||
rc = tcGetSkinTemperatureMilliC(&millis);
|
||||
ASSERT_RESULT_OK(rc, "tcGetSkinTemperatureMilliC");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HocClkThermalSensor_Battery: {
|
||||
batteryInfoGetChargeInfo(&info);
|
||||
millis = batteryInfoGetTemperatureMiliCelsius(&info);
|
||||
break;
|
||||
}
|
||||
case HocClkThermalSensor_PMIC: {
|
||||
millis = 50000;
|
||||
break;
|
||||
}
|
||||
case HocClkThermalSensor_CPU: {
|
||||
millis = temps.cpu;
|
||||
break;
|
||||
}
|
||||
case HocClkThermalSensor_GPU: {
|
||||
millis = temps.gpu;
|
||||
break;
|
||||
}
|
||||
case HocClkThermalSensor_MEM: {
|
||||
millis = board::GetSocType() == HocClkSocType_Mariko ? temps.pllx : temps.mem;
|
||||
break;
|
||||
}
|
||||
case HocClkThermalSensor_PLLX: {
|
||||
millis = temps.pllx;
|
||||
}
|
||||
default: {
|
||||
ASSERT_ENUM_VALID(HocClkThermalSensor, sensor);
|
||||
}
|
||||
}
|
||||
|
||||
return std::max(0, millis);
|
||||
}
|
||||
|
||||
s32 GetPowerMw(HocClkPowerSensor sensor) {
|
||||
switch (sensor) {
|
||||
case HocClkPowerSensor_Now:
|
||||
return max17050PowerNow();
|
||||
case HocClkPowerSensor_Avg:
|
||||
return max17050PowerAvg();
|
||||
default:
|
||||
ASSERT_ENUM_VALID(HocClkPowerSensor, sensor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <hocclk.h>
|
||||
#include <switch.h>
|
||||
|
||||
namespace board {
|
||||
|
||||
s32 GetTemperatureMilli(HocClkThermalSensor sensor);
|
||||
s32 GetPowerMw(HocClkPowerSensor sensor);
|
||||
|
||||
}
|
||||
@@ -1,445 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include <memmem.h>
|
||||
#include <registers.h>
|
||||
#include <cstring>
|
||||
#include <rgltr.h>
|
||||
#include <battery.h>
|
||||
#include "board.hpp"
|
||||
#include "board_freq.hpp"
|
||||
#include "board_volt.hpp"
|
||||
#include "../file_utils.hpp"
|
||||
|
||||
namespace board {
|
||||
|
||||
GpuVoltData voltData = {};
|
||||
u64 cldvfs;
|
||||
CpuDfllData cachedTune;
|
||||
|
||||
/* ... This really needs some cleanup... */
|
||||
namespace {
|
||||
struct EristaCpuUvEntry {
|
||||
u32 tune0;
|
||||
u32 tune1;
|
||||
};
|
||||
|
||||
struct MarikoCpuUvEntry {
|
||||
u32 tune0_low;
|
||||
u32 tune0_high;
|
||||
u32 tune1_low;
|
||||
u32 tune1_high;
|
||||
};
|
||||
|
||||
EristaCpuUvEntry eristaCpuUvTable[5] = {
|
||||
{0xffff, 0x27007ff},
|
||||
{0xefff, 0x27407ff},
|
||||
{0xdfff, 0x27807ff},
|
||||
{0xdfdf, 0x27a07ff},
|
||||
{0xcfdf, 0x37007ff},
|
||||
};
|
||||
|
||||
MarikoCpuUvEntry marikoCpuUvLow[12] = {
|
||||
{0xffa0, 0xffff, 0x21107ff, 0},
|
||||
{0x0, 0xffdf, 0x21107ff, 0x27207ff},
|
||||
{0xffdf, 0xffdf, 0x21107ff, 0x27307ff},
|
||||
{0xffff, 0xffdf, 0x21107ff, 0x27407ff},
|
||||
{0x0, 0xffdf, 0x21607ff, 0x27707ff},
|
||||
{0x0, 0xffdf, 0x21607ff, 0x27807ff},
|
||||
{0x0, 0xdfff, 0x21607ff, 0x27b07ff},
|
||||
{0xdfff, 0xdfff, 0x21707ff, 0x27b07ff},
|
||||
{0xdfff, 0xdfff, 0x21707ff, 0x27c07ff},
|
||||
{0xdfff, 0xdfff, 0x21707ff, 0x27d07ff},
|
||||
{0xdfff, 0xdfff, 0x21707ff, 0x27e07ff},
|
||||
{0xdfff, 0xdfff, 0x21707ff, 0x27f07ff},
|
||||
};
|
||||
|
||||
MarikoCpuUvEntry marikoCpuUvHigh[12] = {
|
||||
{0x0, 0xffff, 0, 0},
|
||||
{0x0, 0xffdf, 0, 0x27207ff},
|
||||
{0x0, 0xffdf, 0, 0x27307ff},
|
||||
{0x0, 0xffdf, 0, 0x27407ff},
|
||||
{0x0, 0xffdf, 0, 0x27707ff},
|
||||
{0x0, 0xffdf, 0, 0x27807ff},
|
||||
{0x0, 0xdfff, 0, 0x27b07ff},
|
||||
{0x0, 0xdfff, 0, 0x27c07ff},
|
||||
{0x0, 0xdfff, 0, 0x27d07ff},
|
||||
{0x0, 0xdfff, 0, 0x27e07ff},
|
||||
{0x0, 0xdfff, 0, 0x27f07ff},
|
||||
{0x0, 0xdfff, 0, 0x27f07ff},
|
||||
};
|
||||
}
|
||||
|
||||
void CacheDfllData() {
|
||||
Result rc = QueryMemoryMapping(&cldvfs, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (cldvfs)");
|
||||
|
||||
if (GetSocType() == HocClkSocType_Erista) {
|
||||
cachedTune.tune0Low = *reinterpret_cast<u32 *>(cldvfs + CL_DVFS_TUNE0_0);
|
||||
cachedTune.tune1Low = *reinterpret_cast<u32 *>(cldvfs + CL_DVFS_TUNE1_0);
|
||||
} else {
|
||||
SetHz(HocClkModule_CPU, 1785000000);
|
||||
cachedTune.tune0High = *reinterpret_cast<u32 *>(cldvfs + CL_DVFS_TUNE0_0);
|
||||
ResetToStockCpu();
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: clean up this code. */
|
||||
void SetDfllTunings(u32 levelLow, u32 levelHigh, u32 tbreakPoint) {
|
||||
u32* tune0_ptr = reinterpret_cast<u32 *>(cldvfs + CL_DVFS_TUNE0_0);
|
||||
u32* tune1_ptr = reinterpret_cast<u32 *>(cldvfs + CL_DVFS_TUNE1_0);
|
||||
if (GetSocType() == HocClkSocType_Mariko) {
|
||||
if (GetHz(HocClkModule_CPU) < tbreakPoint && (levelLow || levelHigh)) {
|
||||
if (levelLow) {
|
||||
*tune0_ptr = marikoCpuUvLow[levelLow-1].tune0_low;
|
||||
*tune1_ptr = marikoCpuUvLow[levelLow-1].tune1_low;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
if (levelLow) {
|
||||
*tune0_ptr = marikoCpuUvLow[levelLow-1].tune0_low;
|
||||
*tune1_ptr = marikoCpuUvLow[levelLow-1].tune1_low;
|
||||
}
|
||||
if (levelHigh) {
|
||||
*tune0_ptr = marikoCpuUvHigh[levelHigh-1].tune0_high;
|
||||
*tune1_ptr = marikoCpuUvHigh[levelHigh-1].tune1_high;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (GetHz(HocClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak
|
||||
*tune0_ptr = 0xCFFF;
|
||||
*tune1_ptr = 0xFF072201;
|
||||
return;
|
||||
} else if (GetHz(HocClkModule_CPU) >= tbreakPoint || (!levelHigh)) {
|
||||
*tune0_ptr = cachedTune.tune0High; // per console?
|
||||
*tune1_ptr = 0xFFF7FF3F;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (GetHz(HocClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak
|
||||
*tune0_ptr = cachedTune.tune0Low; // I think each erista has a different tune0/tune1?
|
||||
*tune1_ptr = cachedTune.tune1Low;
|
||||
return;
|
||||
} else {
|
||||
if (levelLow) {
|
||||
*tune0_ptr = eristaCpuUvTable[levelLow-1].tune0;
|
||||
*tune1_ptr = eristaCpuUvTable[levelLow-1].tune1;
|
||||
} else {
|
||||
*tune0_ptr = 0x0;
|
||||
*tune1_ptr = 0x0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
enum TableConfig: u32 {
|
||||
DEFAULT_TABLE = 1,
|
||||
TBREAK_1581 = 2,
|
||||
TBREAK_1683 = 3,
|
||||
EXTREME_TABLE = 4,
|
||||
};
|
||||
*/
|
||||
u32 CalculateTbreak(u32 table) {
|
||||
if (GetSocType() == HocClkSocType_Erista) {
|
||||
return 1581000000;
|
||||
} else {
|
||||
switch (table) {
|
||||
case 1 ... 2:
|
||||
case 4:
|
||||
return 1581000000;
|
||||
case 3:
|
||||
return 1683000000;
|
||||
default:
|
||||
return 1581000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch Power domains (max77620):
|
||||
* Name | Usage | uV step | uV min | uV default | uV max | Init
|
||||
*-------+---------------+---------+--------+------------+---------+------------------
|
||||
* sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1)
|
||||
* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1)
|
||||
* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv)
|
||||
* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 |
|
||||
* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1)
|
||||
* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv)
|
||||
* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 |
|
||||
* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv)
|
||||
* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | 0.85V (AO, pcv)
|
||||
* ldo5 | GC Card | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv)
|
||||
* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V (pcv)
|
||||
* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | 1.05V (pcv)
|
||||
* ldo8 | XUSB, DP, MCU | 50000 | 800000 | 1050000 | 2800000 | 1.05V/2.8V (pcv)
|
||||
|
||||
typedef enum {
|
||||
PcvPowerDomainId_Max77620_Sd0 = 0x3A000080,
|
||||
PcvPowerDomainId_Max77620_Sd1 = 0x3A000081, // vdd2
|
||||
PcvPowerDomainId_Max77620_Sd2 = 0x3A000082,
|
||||
PcvPowerDomainId_Max77620_Sd3 = 0x3A000083,
|
||||
PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0,
|
||||
PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1,
|
||||
PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2,
|
||||
PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3,
|
||||
PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4,
|
||||
PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5,
|
||||
PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6,
|
||||
PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7,
|
||||
PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8,
|
||||
PcvPowerDomainId_Max77621_Cpu = 0x3A000003,
|
||||
PcvPowerDomainId_Max77621_Gpu = 0x3A000004,
|
||||
PcvPowerDomainId_Max77812_Cpu = 0x3A000003,
|
||||
PcvPowerDomainId_Max77812_Gpu = 0x3A000004,
|
||||
PcvPowerDomainId_Max77812_Dram = 0x3A000005, // vddq
|
||||
} PowerDomainId;
|
||||
*/
|
||||
u32 GetVoltage(HocClkVoltage voltage) {
|
||||
RgltrSession session;
|
||||
Result rc = 0;
|
||||
u32 out = 0;
|
||||
BatteryChargeInfo info;
|
||||
|
||||
switch (voltage) {
|
||||
case HocClkVoltage_SOC:
|
||||
rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Sd0);
|
||||
ASSERT_RESULT_OK(rc, "rgltrOpenSession")
|
||||
rgltrGetVoltage(&session, &out);
|
||||
rgltrCloseSession(&session);
|
||||
break;
|
||||
case HocClkVoltage_EMCVDD2:
|
||||
rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Sd1);
|
||||
ASSERT_RESULT_OK(rc, "rgltrOpenSession")
|
||||
rgltrGetVoltage(&session, &out);
|
||||
rgltrCloseSession(&session);
|
||||
break;
|
||||
case HocClkVoltage_CPU:
|
||||
if (GetSocType() == HocClkSocType_Mariko) {
|
||||
rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77621_Cpu);
|
||||
} else {
|
||||
rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Cpu);
|
||||
}
|
||||
ASSERT_RESULT_OK(rc, "rgltrOpenSession")
|
||||
rgltrGetVoltage(&session, &out);
|
||||
rgltrCloseSession(&session);
|
||||
break;
|
||||
case HocClkVoltage_GPU:
|
||||
if (GetSocType() == HocClkSocType_Mariko) {
|
||||
rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77621_Gpu);
|
||||
} else {
|
||||
rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Gpu);
|
||||
}
|
||||
ASSERT_RESULT_OK(rc, "rgltrOpenSession")
|
||||
rgltrGetVoltage(&session, &out);
|
||||
rgltrCloseSession(&session);
|
||||
break;
|
||||
case HocClkVoltage_EMCVDDQ:
|
||||
if (GetSocType() == HocClkSocType_Mariko) {
|
||||
rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Dram);
|
||||
ASSERT_RESULT_OK(rc, "rgltrOpenSession")
|
||||
rgltrGetVoltage(&session, &out);
|
||||
rgltrCloseSession(&session);
|
||||
} else {
|
||||
out = GetVoltage(HocClkVoltage_EMCVDD2);
|
||||
}
|
||||
break;
|
||||
case HocClkVoltage_Display:
|
||||
rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Ldo0);
|
||||
ASSERT_RESULT_OK(rc, "rgltrOpenSession")
|
||||
rgltrGetVoltage(&session, &out);
|
||||
rgltrCloseSession(&session);
|
||||
break;
|
||||
case HocClkVoltage_Battery:
|
||||
batteryInfoGetChargeInfo(&info);
|
||||
out = info.VoltageAvg;
|
||||
break;
|
||||
default:
|
||||
ASSERT_ENUM_VALID(HocClkVoltage, voltage);
|
||||
}
|
||||
|
||||
return out > 0 ? out : 0;
|
||||
}
|
||||
|
||||
Handle GetPcvHandle() {
|
||||
constexpr u64 PcvID = 0x10000000000001a;
|
||||
u64 processIDList[80]{};
|
||||
s32 processCount = 0;
|
||||
Handle handle = INVALID_HANDLE;
|
||||
|
||||
DebugEventInfo debugEvent{};
|
||||
|
||||
/* Get all running processes. */
|
||||
Result resultGetProcessList = svcGetProcessList(&processCount, processIDList, std::size(processIDList));
|
||||
if (R_FAILED(resultGetProcessList)) {
|
||||
return INVALID_HANDLE;
|
||||
}
|
||||
|
||||
/* Try to find pcv. */
|
||||
for (int i = 0; i < processCount; ++i) {
|
||||
if (handle != INVALID_HANDLE) {
|
||||
svcCloseHandle(handle);
|
||||
handle = INVALID_HANDLE;
|
||||
}
|
||||
|
||||
/* Try to debug process, if it fails, try next process. */
|
||||
Result resultSvcDebugProcess = svcDebugActiveProcess(&handle, processIDList[i]);
|
||||
if (R_FAILED(resultSvcDebugProcess)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Try to get a debug event. */
|
||||
Result resultDebugEvent = svcGetDebugEvent(&debugEvent, handle);
|
||||
if (R_SUCCEEDED(resultDebugEvent)) {
|
||||
if (debugEvent.info.create_process.program_id == PcvID) {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Failed to get handle. */
|
||||
return INVALID_HANDLE;
|
||||
}
|
||||
|
||||
void CacheGpuVoltTable() {
|
||||
UnkRegulator reg = {
|
||||
.voltageMin = 600000,
|
||||
.voltageStep = 12500,
|
||||
.voltageMax = 1400000,
|
||||
};
|
||||
|
||||
Handle handle = GetPcvHandle();
|
||||
if (handle == INVALID_HANDLE) {
|
||||
fileUtils::LogLine("[dvfs] Invalid handle!");
|
||||
return;
|
||||
}
|
||||
|
||||
MemoryInfo memoryInfo = {};
|
||||
u64 address = 0;
|
||||
u32 pageInfo = 0;
|
||||
constexpr u32 PageSize = 0x1000;
|
||||
u8 buffer[PageSize];
|
||||
|
||||
/* Loop until failure. */
|
||||
while (true) {
|
||||
/* Find pcv heap. */
|
||||
while (true) {
|
||||
Result resultProcessMemory = svcQueryDebugProcessMemory(&memoryInfo, &pageInfo, handle, address);
|
||||
address = memoryInfo.addr + memoryInfo.size;
|
||||
|
||||
if (R_FAILED(resultProcessMemory) || !address) {
|
||||
svcCloseHandle(handle);
|
||||
fileUtils::LogLine("[dvfs] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory));
|
||||
handle = INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (memoryInfo.size && (memoryInfo.perm & 3) == 3 && static_cast<char>(memoryInfo.type) == 0x4) {
|
||||
/* Found valid memory. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (u64 base = 0; base < memoryInfo.size; base += PageSize) {
|
||||
u32 memorySize = std::min(memoryInfo.size, static_cast<u64>(PageSize));
|
||||
if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, base + memoryInfo.addr, memorySize))) {
|
||||
break;
|
||||
}
|
||||
|
||||
u8 *resultFindReg = static_cast<u8 *>(memmem_impl(buffer, sizeof(buffer), ®, sizeof(reg)));
|
||||
u32 index = resultFindReg - buffer;
|
||||
|
||||
if (!resultFindReg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Assuming mariko. */
|
||||
const u32 vmax = 800;
|
||||
constexpr u32 VoltageTableOffset = 312;
|
||||
if (!std::memcmp(&buffer[index + VoltageTableOffset], &vmax, sizeof(vmax))) {
|
||||
std::memcpy(voltData.voltTable, &buffer[index + VoltageTableOffset], sizeof(voltData.voltTable));
|
||||
voltData.voltTableAddress = base + memoryInfo.addr + VoltageTableOffset + index;
|
||||
}
|
||||
|
||||
svcCloseHandle(handle);
|
||||
handle = INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
svcCloseHandle(handle);
|
||||
handle = INVALID_HANDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
void PcvHijackGpuVolts(u32 vmin) {
|
||||
u32 table[192];
|
||||
static_assert(sizeof(table) == sizeof(voltData.voltTable), "Invalid gpu voltage table size!");
|
||||
std::memcpy(table, voltData.voltTable, sizeof(voltData.voltTable));
|
||||
|
||||
if (voltData.ramVmin == vmin) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < std::size(table); ++i) {
|
||||
if (table[i] && table[i] <= vmin) {
|
||||
table[i] = vmin;
|
||||
}
|
||||
}
|
||||
|
||||
Handle handle = GetPcvHandle();
|
||||
if (handle == INVALID_HANDLE) {
|
||||
fileUtils::LogLine("Invalid handle!");
|
||||
return;
|
||||
}
|
||||
|
||||
Result rc = svcWriteDebugProcessMemory(handle, table, voltData.voltTableAddress, sizeof(table));
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
voltData.ramVmin = vmin;
|
||||
}
|
||||
|
||||
svcCloseHandle(handle);
|
||||
fileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
|
||||
}
|
||||
|
||||
u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket) {
|
||||
static const u32 ramTable[][22] = {
|
||||
{ 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, },
|
||||
{ 2300, 2366, 2433, 2466, 2533, 2566, 2633, 2700, 2733, 2800, 2833, 2900, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3266, },
|
||||
{ 2433, 2466, 2533, 2600, 2666, 2733, 2766, 2800, 2833, 2866, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3300, 3333, 3366, },
|
||||
{ 2500, 2533, 2600, 2633, 2666, 2733, 2800, 2866, 2900, 2966, 3033, 3100, 3166, 3200, 3233, 3266, 3300, 3333, 3366, 3400, 3400, 3400, },
|
||||
};
|
||||
|
||||
static const u32 gpuVoltArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, };
|
||||
|
||||
if (freqMhz <= 1600) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < std::size(gpuVoltArray); ++i) {
|
||||
if (freqMhz <= ramTable[bracket][i]) {
|
||||
return gpuVoltArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
return gpuVoltArray[std::size(gpuVoltArray) - 1];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
|
||||
namespace board {
|
||||
|
||||
struct GpuVoltData {
|
||||
u32 voltTable[6][32] = {};
|
||||
u64 voltTableAddress;
|
||||
u32 ramVmin;
|
||||
};
|
||||
|
||||
/* TODO: Find out what component this actually targets. */
|
||||
struct UnkRegulator {
|
||||
u32 voltageMin;
|
||||
u32 voltageStep;
|
||||
u32 voltageMax;
|
||||
};
|
||||
|
||||
struct CpuDfllData {
|
||||
u32 tune0Low;
|
||||
u32 tune0High;
|
||||
u32 tune1Low;
|
||||
u32 tune1High;
|
||||
// u32 tune_high_min_millivolts;
|
||||
// u32 tune_high_margin_millivolts;
|
||||
// u64 dvco_calibration_max;
|
||||
};
|
||||
|
||||
void SetDfllTunings(u32 levelLow, u32 levelHigh, u32 tbreakPoint);
|
||||
void CacheDfllData();
|
||||
u32 CalculateTbreak(u32 table);
|
||||
u32 GetVoltage(HocClkVoltage voltage);
|
||||
void CacheGpuVoltTable();
|
||||
void PcvHijackGpuVolts(u32 vmin);
|
||||
u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket);
|
||||
|
||||
}
|
||||
@@ -1,736 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, based on reasearch by MasaGratoR and Cooler3D
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "display_refresh_rate.hpp"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <switch.h>
|
||||
|
||||
namespace display {
|
||||
#define DSI_CLOCK_HZ 234000000llu
|
||||
#define NVDISP_GET_MODE2 0x803C021B
|
||||
#define NVDISP_SET_MODE2 0x403C021C
|
||||
#define NVDISP_VALIDATE_MODE2 0xC03C021D
|
||||
#define NVDISP_GET_MODE_DB2 0xEF20021E
|
||||
#define NVDISP_GET_PANEL_DATA 0xC01C0226
|
||||
|
||||
#define MAX_REFRESH_RATE 72
|
||||
|
||||
static DisplayRefreshConfig g_config = {0};
|
||||
static bool g_initialized = false;
|
||||
|
||||
static uint8_t g_dockedHighestRefreshRate = 60;
|
||||
static uint8_t g_dockedLinkRate = 10;
|
||||
static bool g_wasRetroSuperTurnedOff = false;
|
||||
static uint32_t g_lastVActive = 1080;
|
||||
static bool g_canChangeRefreshRateDocked = false;
|
||||
static uint8_t g_lastVActiveSet = 0;
|
||||
|
||||
static const uint8_t g_dockedRefreshRates[] = {40, 45, 50, 55, 60, 70, 72, 75, 80, 90, 95, 100, 110, 120, 130, 140, 144, 150, 160, 165, 170, 180, 190, 200, 210, 220, 230, 240};
|
||||
// Calculate with this tool:
|
||||
|
||||
// https://tomverbeure.github.io/video_timings_calculator?horiz_pixels=1920&vert_pixels=1080&refresh_rate=240&margins=false&interlaced=false&bpc=8&color_fmt=rgb444&video_opt=false&custom_hblank=80&custom_vblank=6
|
||||
|
||||
/*
|
||||
typedef struct {
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hSyncWidth;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vFrontPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint8_t vBackPorch;
|
||||
uint8_t VIC;
|
||||
uint32_t pixelClock_kHz;
|
||||
} DockedTimings;
|
||||
*/
|
||||
static const DockedTimings g_dockedTimings1080p[] = {
|
||||
{8, 32, 40, 7, 8, 6, 0, 88080}, // 40Hz
|
||||
{8, 32, 40, 9, 8, 6, 0, 99270}, // 45Hz
|
||||
{528, 44, 148, 4, 5, 36, 31, 148500}, // 50Hz
|
||||
{8, 32, 40, 15, 8, 6, 0, 121990}, // 55Hz
|
||||
{88, 44, 148, 4, 5, 36, 16, 148500}, // 60Hz
|
||||
{8, 32, 40, 22, 8, 6, 0, 156240}, // 70Hz
|
||||
{8, 32, 40, 23, 8, 6, 0, 160848}, // 72Hz
|
||||
{8, 32, 40, 25, 8, 6, 0, 167850}, // 75Hz
|
||||
{8, 32, 40, 28, 8, 6, 0, 179520}, // 80Hz
|
||||
{8, 32, 40, 33, 8, 6, 0, 202860}, // 90Hz
|
||||
{8, 32, 40, 36, 8, 6, 0, 214700}, // 95Hz
|
||||
{528, 44, 148, 4, 5, 36, 64, 297000}, // 100Hz
|
||||
{8, 32, 40, 44, 8, 6, 0, 250360}, // 110Hz
|
||||
{88, 44, 148, 4, 5, 36, 63, 297000}, // 120Hz
|
||||
{8, 32, 40, 55, 8, 6, 0, 298750}, //130Hz CVT-RBv2
|
||||
{8, 32, 40, 61, 8, 6, 0, 323400}, //140Hz CVT-RBv2
|
||||
{8, 32, 40, 63, 8, 6, 0, 333216}, //144Hz CVT-RBv2
|
||||
{8, 32, 40, 67, 8, 6, 0, 348300}, //150Hz CVT-RBv2
|
||||
{8, 32, 40, 72, 8, 6, 0, 373120}, //160Hz CVT-RBv2
|
||||
{8, 32, 40, 75, 8, 6, 0, 385770}, //165Hz CVT-RBv2
|
||||
{8, 32, 40, 78, 8, 6, 0, 398480}, //170Hz CVT-RBv2
|
||||
{8, 32, 40, 84, 8, 6, 0, 424080}, //180Hz CVT-RBv2
|
||||
{8, 32, 40, 90, 8, 6, 0, 449920}, //190Hz CVT-RBv2
|
||||
{8, 32, 40, 96, 8, 6, 0, 476000}, //200Hz CVT-RBv2
|
||||
{8, 32, 40, 102, 8, 6, 0, 502320}, //210Hz CVT-RBv2
|
||||
{8, 32, 40, 108, 8, 6, 0, 528880}, //220Hz CVT-RBv2
|
||||
{8, 32, 40, 114, 8, 6, 0, 555680}, //230Hz CVT-RBv2
|
||||
{8, 32, 40, 121, 8, 6, 0, 583200}, //240Hz CVT-RBv2
|
||||
// technically you can go to 476hz, but in practice, why would you?
|
||||
};
|
||||
|
||||
// These timings *should* work but are untested
|
||||
static const HandheldTimings g_handheldTimingsRETRO[] = {
|
||||
{72, 136, 72, 1, 660, 9, 78000}, // 40Hz
|
||||
{72, 136, 72, 1, 612, 9, 77982}, // 41Hz
|
||||
{72, 136, 72, 1, 567, 9, 77994}, // 42Hz
|
||||
{72, 136, 72, 1, 524, 9, 78002}, // 43Hz
|
||||
{72, 136, 72, 1, 483, 9, 78012}, // 44Hz
|
||||
{72, 136, 72, 1, 443, 9, 77985}, // 45Hz
|
||||
{72, 136, 72, 1, 406, 9, 78016}, // 46Hz
|
||||
{72, 136, 72, 1, 370, 9, 78020}, // 47Hz
|
||||
{72, 136, 72, 1, 335, 9, 78000}, // 48Hz
|
||||
{72, 136, 72, 1, 302, 9, 78008}, // 49Hz
|
||||
{72, 136, 72, 1, 270, 9, 78000}, // 50Hz
|
||||
{72, 136, 72, 1, 239, 9, 77979}, // 51Hz
|
||||
{72, 136, 72, 1, 210, 9, 78000}, // 52Hz
|
||||
{72, 136, 72, 1, 182, 9, 78016}, // 53Hz
|
||||
{72, 136, 72, 1, 154, 9, 77976}, // 54Hz
|
||||
{72, 136, 72, 1, 128, 9, 77990}, // 55Hz
|
||||
{72, 136, 72, 1, 103, 9, 78008}, // 56Hz
|
||||
{72, 136, 72, 1, 78, 9, 77976}, // 57Hz
|
||||
{72, 136, 72, 1, 55, 9, 78010}, // 58Hz
|
||||
{72, 136, 72, 1, 32, 9, 77998}, // 59Hz
|
||||
{72, 136, 72, 1, 10, 9, 78000}, // 60Hz
|
||||
};
|
||||
|
||||
static const MinMaxRefreshRate g_handheldModeRefreshRate = {40, 80};
|
||||
|
||||
static uint8_t _getDockedRefreshRateIterator(uint32_t refreshRate) {
|
||||
for (size_t i = 0; i < sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]); i++) {
|
||||
if (g_dockedRefreshRates[i] == refreshRate) return i;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void _changeOledElvssSettings(const uint32_t* offsets, const uint32_t* value, uint32_t size, uint32_t start) {
|
||||
if (!g_config.dsiVirtAddr || !value || !size) return;
|
||||
|
||||
volatile uint32_t* dsi = (uint32_t*)g_config.dsiVirtAddr;
|
||||
|
||||
#define DSI_VIDEO_MODE_CONTROL 0x4E
|
||||
#define DSI_WR_DATA 0xA
|
||||
#define DSI_TRIGGER 0x13
|
||||
#define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15
|
||||
#define MIPI_DSI_DCS_LONG_WRITE 0x39
|
||||
#define MIPI_DCS_PRIV_SM_SET_REG_OFFSET 0xB0
|
||||
#define MIPI_DCS_PRIV_SM_SET_ELVSS 0xB1
|
||||
|
||||
dsi[DSI_VIDEO_MODE_CONTROL] = true;
|
||||
svcSleepThread(20000000);
|
||||
|
||||
dsi[DSI_WR_DATA] = MIPI_DSI_DCS_LONG_WRITE | (5 << 8);
|
||||
dsi[DSI_WR_DATA] = 0x5A5A5AE2;
|
||||
dsi[DSI_WR_DATA] = 0x5A;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
|
||||
for (size_t i = start; i < size; i++) {
|
||||
dsi[DSI_WR_DATA] = ((MIPI_DCS_PRIV_SM_SET_REG_OFFSET | ((offsets[i] % 0x100) << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
dsi[DSI_WR_DATA] = ((MIPI_DCS_PRIV_SM_SET_ELVSS | (value[i] << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
}
|
||||
|
||||
dsi[DSI_WR_DATA] = MIPI_DSI_DCS_LONG_WRITE | (5 << 8);
|
||||
dsi[DSI_WR_DATA] = 0xA55A5AE2;
|
||||
dsi[DSI_WR_DATA] = 0xA5;
|
||||
dsi[DSI_TRIGGER] = 0;
|
||||
|
||||
dsi[DSI_VIDEO_MODE_CONTROL] = false;
|
||||
svcSleepThread(20000000);
|
||||
}
|
||||
void SetDockedState(bool isDocked) {
|
||||
g_config.isDocked = isDocked;
|
||||
}
|
||||
|
||||
bool Initialize(const DisplayRefreshConfig* config) {
|
||||
if (!config) return false;
|
||||
|
||||
g_config = *config;
|
||||
g_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CorrectOledGamma(uint32_t refresh_rate) {
|
||||
static uint32_t last_refresh_rate = 60;
|
||||
static int counter = 0;
|
||||
|
||||
if (g_config.isDocked || refresh_rate < 45 || refresh_rate > 60) {
|
||||
last_refresh_rate = 60;
|
||||
return;
|
||||
}
|
||||
|
||||
if (counter != 9) {
|
||||
counter++;
|
||||
return;
|
||||
}
|
||||
counter = 0;
|
||||
|
||||
uint32_t offsets[] = {0x1A, 0x24, 0x25};
|
||||
uint32_t values[] = {2, 0, 0x83};
|
||||
|
||||
if (refresh_rate == 60) {
|
||||
if (last_refresh_rate == 60) return;
|
||||
} else if (refresh_rate == 45) {
|
||||
if (last_refresh_rate == 45) return;
|
||||
uint32_t vals[] = {4, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else if (refresh_rate == 50) {
|
||||
if (last_refresh_rate == 50) return;
|
||||
uint32_t vals[] = {3, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else if (refresh_rate == 55) {
|
||||
if (last_refresh_rate == 55) return;
|
||||
uint32_t vals[] = {3, 1, 0};
|
||||
memcpy(values, vals, sizeof(vals));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
_changeOledElvssSettings(offsets, values, 3, 0);
|
||||
}
|
||||
last_refresh_rate = refresh_rate;
|
||||
}
|
||||
|
||||
void SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p) {
|
||||
// Function kept for API compatibility but does nothing
|
||||
(void)refreshRates;
|
||||
(void)is720p;
|
||||
}
|
||||
|
||||
uint8_t GetDockedHighestAllowed(void) {
|
||||
return (g_dockedHighestRefreshRate > 60) ? g_dockedHighestRefreshRate : 60;
|
||||
}
|
||||
|
||||
static void _getDockedHighestRefreshRate(uint32_t fd_in) {
|
||||
uint8_t highestRefreshRate = 60;
|
||||
uint32_t fd = fd_in;
|
||||
|
||||
if(!fd) nvOpen(&fd, "/dev/nvdisp-disp1");
|
||||
NvdcModeDB2 db2 = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE_DB2, &db2);
|
||||
|
||||
if (rc == 0) {
|
||||
for (size_t i = 0; i < db2.num_modes; i++) {
|
||||
if (db2.modes[i].hActive < 1920 || db2.modes[i].vActive < 1080)
|
||||
continue;
|
||||
|
||||
uint32_t v_total = db2.modes[i].vActive + db2.modes[i].vSyncWidth + db2.modes[i].vFrontPorch + db2.modes[i].vBackPorch;
|
||||
uint32_t h_total = db2.modes[i].hActive + db2.modes[i].hSyncWidth + db2.modes[i].hFrontPorch + db2.modes[i].hBackPorch;
|
||||
double refreshRate = round((double)(db2.modes[i].pclkKHz * 1000) / (double)(v_total * h_total));
|
||||
|
||||
if (highestRefreshRate < (uint8_t)refreshRate)
|
||||
highestRefreshRate = (uint8_t)refreshRate;
|
||||
}
|
||||
} else {
|
||||
g_dockedHighestRefreshRate = 60;
|
||||
}
|
||||
|
||||
const size_t numRates = sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]);
|
||||
if (highestRefreshRate > g_dockedRefreshRates[numRates - 1])
|
||||
highestRefreshRate = g_dockedRefreshRates[numRates - 1];
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
|
||||
struct dpaux_read_0x100 {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char downspread;
|
||||
unsigned char training_pattern;
|
||||
unsigned char lane_pattern[4];
|
||||
unsigned char unk2[8];
|
||||
} set;
|
||||
} dpaux = {6, 0x100, 0x10};
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
if (rc == 0) {
|
||||
g_dockedLinkRate = dpaux.set.link_rate;
|
||||
// if (display_b.hActive == 1920 && display_b.vActive == 1080 && highestRefreshRate > 75 && dpaux.set.link_rate < 20 && )
|
||||
// highestRefreshRate = 75;
|
||||
}
|
||||
|
||||
if (!fd_in) nvClose(fd);
|
||||
g_dockedHighestRefreshRate = highestRefreshRate;
|
||||
}
|
||||
|
||||
static bool _setPLLDHandheldRefreshRate(uint32_t new_refreshRate) {
|
||||
if (!g_config.clkVirtAddr) return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dpaux_read {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned int rev_minor : 4;
|
||||
unsigned int rev_major : 4;
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char unk2[13];
|
||||
} DPCD;
|
||||
} dpaux = {6, 0, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
if (rc != 0x75c) return false;
|
||||
|
||||
PLLD_BASE base = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&base, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
uint32_t value = ((base.PLLD_DIVN / base.PLLD_DIVM) * 10) / 4;
|
||||
if (value == 0 || value == 80) return false;
|
||||
|
||||
if (new_refreshRate > g_handheldModeRefreshRate.max) {
|
||||
new_refreshRate = g_handheldModeRefreshRate.max;
|
||||
} else if (new_refreshRate < g_handheldModeRefreshRate.min) {
|
||||
bool skip = false;
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (new_refreshRate * i == 60) {
|
||||
skip = true;
|
||||
new_refreshRate = 60;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (((new_refreshRate * i) >= g_handheldModeRefreshRate.min) && ((new_refreshRate * i) <= g_handheldModeRefreshRate.max)) {
|
||||
skip = true;
|
||||
new_refreshRate *= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skip) new_refreshRate = 60;
|
||||
}
|
||||
|
||||
uint32_t pixelClock = (9375 * ((4096 * ((2 * base.PLLD_DIVN) + 1)) + misc.PLLD_SDM_DIN)) / (8 * base.PLLD_DIVM);
|
||||
uint16_t refreshRateNow = pixelClock / (DSI_CLOCK_HZ / 60);
|
||||
|
||||
if (refreshRateNow == new_refreshRate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t base_refreshRate = new_refreshRate - (new_refreshRate % 5);
|
||||
base.PLLD_DIVN = (4 * base_refreshRate) / 10;
|
||||
base.PLLD_DIVM = 1;
|
||||
|
||||
uint64_t expected_pixel_clock = (DSI_CLOCK_HZ * new_refreshRate) / 60;
|
||||
misc.PLLD_SDM_DIN = ((8 * base.PLLD_DIVM * expected_pixel_clock) / 9375) - (4096 * ((2 * base.PLLD_DIVN) + 1));
|
||||
|
||||
memcpy((void*)(g_config.clkVirtAddr + 0xD0), &base, 4);
|
||||
memcpy((void*)(g_config.clkVirtAddr + 0xDC), &misc, 4);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _setNvDispDockedRefreshRate(uint32_t new_refreshRate) {
|
||||
if (g_config.isLite || !g_canChangeRefreshRateDocked)
|
||||
return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
if (rc != 0) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!((display_b.vActive == 480 && display_b.hActive == 720) ||
|
||||
(display_b.vActive == 720 && display_b.hActive == 1280) ||
|
||||
(display_b.vActive == 1080 && display_b.hActive == 1920))) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (display_b.vActive != g_lastVActiveSet) {
|
||||
g_lastVActiveSet = display_b.vActive;
|
||||
}
|
||||
|
||||
uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint32_t refreshRateNow = ((display_b.pclkKHz) * 1000 + 999) / (h_total * v_total);
|
||||
|
||||
int8_t itr = -1;
|
||||
const size_t numRates = sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]);
|
||||
|
||||
// Find closest matching refresh rate
|
||||
if ((new_refreshRate <= 60) && ((60 % new_refreshRate) == 0)) {
|
||||
itr = _getDockedRefreshRateIterator(60);
|
||||
}
|
||||
|
||||
if (itr == -1) {
|
||||
for (size_t i = 0; i < numRates; i++) {
|
||||
uint8_t val = g_dockedRefreshRates[i];
|
||||
if ((val % new_refreshRate) == 0) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itr == -1) {
|
||||
if (!g_config.matchLowestDocked) {
|
||||
itr = _getDockedRefreshRateIterator(60);
|
||||
} else {
|
||||
for (size_t i = 0; i < numRates; i++) {
|
||||
if (new_refreshRate < g_dockedRefreshRates[i]) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itr == -1) itr = _getDockedRefreshRateIterator(60);
|
||||
|
||||
// Clamp to highest allowed refresh rate
|
||||
if (g_dockedRefreshRates[itr] > g_dockedHighestRefreshRate) {
|
||||
for (int8_t i = itr; i >= 0; i--) {
|
||||
if (g_dockedRefreshRates[i] <= g_dockedHighestRefreshRate) {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshRateNow == g_dockedRefreshRates[itr]) {
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itr >= 0 && itr < (int8_t)numRates) {
|
||||
if (display_b.vActive == 720) {
|
||||
uint32_t clock = ((h_total * v_total) * g_dockedRefreshRates[itr]) / 1000;
|
||||
display_b.pclkKHz = clock;
|
||||
} else {
|
||||
display_b.hFrontPorch = g_dockedTimings1080p[itr].hFrontPorch;
|
||||
display_b.hSyncWidth = g_dockedTimings1080p[itr].hSyncWidth;
|
||||
display_b.hBackPorch = g_dockedTimings1080p[itr].hBackPorch;
|
||||
display_b.vFrontPorch = g_dockedTimings1080p[itr].vFrontPorch;
|
||||
display_b.vSyncWidth = g_dockedTimings1080p[itr].vSyncWidth;
|
||||
display_b.vBackPorch = g_dockedTimings1080p[itr].vBackPorch;
|
||||
display_b.pclkKHz = g_dockedTimings1080p[itr].pixelClock_kHz;
|
||||
display_b.vmode = (g_dockedRefreshRates[itr] >= 100 ? 0x400000 : 0x200000);
|
||||
display_b.unk1 = (g_dockedRefreshRates[itr] >= 100 ? 0x80 : 0);
|
||||
display_b.sync = 3;
|
||||
display_b.bitsPerPixel = 24;
|
||||
}
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_VALIDATE_MODE2, &display_b);
|
||||
if (rc == 0) {
|
||||
rc = nvIoctl(fd, NVDISP_SET_MODE2, &display_b);
|
||||
}
|
||||
}
|
||||
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _setNvDispHandheldRefreshRate(uint32_t new_refreshRate) {
|
||||
if (!g_config.isRetroSUPER) return false;
|
||||
|
||||
if (!g_config.displaySync) {
|
||||
g_wasRetroSuperTurnedOff = false;
|
||||
} else if (g_wasRetroSuperTurnedOff) {
|
||||
svcSleepThread(2000000000);
|
||||
g_wasRetroSuperTurnedOff = false;
|
||||
}
|
||||
|
||||
svcSleepThread(1000000000);
|
||||
|
||||
uint32_t fd = 0;
|
||||
if (nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NvdcMode2 display_b = {0};
|
||||
int rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b);
|
||||
if (rc != 0) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((display_b.vActive == 1280 && display_b.hActive == 720) == false) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint32_t refreshRateNow = ((display_b.pclkKHz) * 1000 + 999) / (h_total * v_total);
|
||||
|
||||
if (new_refreshRate > g_handheldModeRefreshRate.max) {
|
||||
new_refreshRate = g_handheldModeRefreshRate.max;
|
||||
} else if (new_refreshRate < g_handheldModeRefreshRate.min) {
|
||||
bool skip = false;
|
||||
for (size_t i = 2; i <= 4; i++) {
|
||||
if (new_refreshRate * i == 60) {
|
||||
skip = true;
|
||||
new_refreshRate = 60;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
for (size_t i = 2; i <= (sizeof(g_handheldTimingsRETRO) / sizeof(g_handheldTimingsRETRO[0])); i++) {
|
||||
if (((new_refreshRate * i) >= g_handheldModeRefreshRate.min) && ((new_refreshRate * i) <= g_handheldModeRefreshRate.max)) {
|
||||
skip = true;
|
||||
new_refreshRate *= i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skip) new_refreshRate = 60;
|
||||
}
|
||||
|
||||
if (new_refreshRate == refreshRateNow) {
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t itr = (new_refreshRate - 40) / 5;
|
||||
display_b.hFrontPorch = g_handheldTimingsRETRO[itr].hFrontPorch;
|
||||
display_b.hSyncWidth = g_handheldTimingsRETRO[itr].hSyncWidth;
|
||||
display_b.hBackPorch = g_handheldTimingsRETRO[itr].hBackPorch;
|
||||
display_b.vFrontPorch = g_handheldTimingsRETRO[itr].vFrontPorch;
|
||||
display_b.vSyncWidth = g_handheldTimingsRETRO[itr].vSyncWidth;
|
||||
display_b.vBackPorch = g_handheldTimingsRETRO[itr].vBackPorch;
|
||||
display_b.pclkKHz = g_handheldTimingsRETRO[itr].pixelClock_kHz;
|
||||
|
||||
rc = nvIoctl(fd, NVDISP_VALIDATE_MODE2, &display_b);
|
||||
if (rc == 0) {
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
nvIoctl(fd, NVDISP_SET_MODE2, &display_b);
|
||||
}
|
||||
}
|
||||
|
||||
nvClose(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetRate(uint32_t new_refreshRate) {
|
||||
if (!new_refreshRate || !g_initialized) return false;
|
||||
|
||||
uint32_t fd = 0;
|
||||
|
||||
if (g_config.isRetroSUPER && !g_config.isDocked) {
|
||||
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
||||
}
|
||||
|
||||
else if ((!g_config.isRetroSUPER && g_config.isLite) || R_FAILED(nvOpen(&fd, "/dev/nvdisp-disp1"))) {
|
||||
if (_setPLLDHandheldRefreshRate(new_refreshRate) == false)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
struct dpaux_read {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned int rev_minor : 4;
|
||||
unsigned int rev_major : 4;
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char unk2[13];
|
||||
} DPCD;
|
||||
} dpaux = {6, 0, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
|
||||
if (rc != 0) {
|
||||
if (!g_config.isRetroSUPER) {
|
||||
return _setPLLDHandheldRefreshRate(new_refreshRate);
|
||||
} else {
|
||||
return _setNvDispHandheldRefreshRate(new_refreshRate);
|
||||
}
|
||||
} else {
|
||||
if(g_config.isDocked)
|
||||
return _setNvDispDockedRefreshRate(new_refreshRate);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetRate(uint32_t* out_refreshRate, bool internal) {
|
||||
if (!out_refreshRate || !g_initialized || !g_config.clkVirtAddr) return false;
|
||||
static uint32_t value = 60;
|
||||
|
||||
if (g_config.isRetroSUPER && !g_config.isDocked) {
|
||||
uint32_t fd = 0;
|
||||
PLLD_BASE temp = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&temp, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
value = ((temp.PLLD_DIVN / temp.PLLD_DIVM) * 10) / 4;
|
||||
|
||||
if (value != 0 && value != 80) {
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp0")) {
|
||||
NvdcMode2 display_b = {0};
|
||||
if (nvIoctl(fd, NVDISP_GET_MODE2, &display_b) == 0) {
|
||||
uint64_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint64_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint64_t pixelClock = display_b.pclkKHz * 1000 + 999;
|
||||
value = (u32)(pixelClock / (h_total * v_total));
|
||||
}
|
||||
nvClose(fd);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
g_wasRetroSuperTurnedOff = true;
|
||||
}
|
||||
}
|
||||
else if ((!g_config.isPossiblySpoofedRetro) || (g_config.isPossiblySpoofedRetro && !g_config.isRetroSUPER)) {
|
||||
PLLD_BASE temp = {0};
|
||||
PLLD_MISC misc = {0};
|
||||
memcpy(&temp, (void*)(g_config.clkVirtAddr + 0xD0), 4);
|
||||
memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4);
|
||||
|
||||
value = ((temp.PLLD_DIVN / temp.PLLD_DIVM) * 10) / 4;
|
||||
|
||||
if (value == 0 || value == 80) {
|
||||
// Docked mode
|
||||
if (g_config.isLite) return false;
|
||||
|
||||
g_config.isDocked = true;
|
||||
|
||||
if (!g_canChangeRefreshRateDocked) {
|
||||
uint32_t fd = 0;
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
struct dpaux_read_0x100 {
|
||||
uint32_t cmd;
|
||||
uint32_t addr;
|
||||
uint32_t size;
|
||||
struct {
|
||||
unsigned char link_rate;
|
||||
unsigned int lane_count: 5;
|
||||
unsigned int unk1: 2;
|
||||
unsigned int isFramingEnhanced: 1;
|
||||
unsigned char downspread;
|
||||
unsigned char training_pattern;
|
||||
unsigned char lane_pattern[4];
|
||||
unsigned char unk2[8];
|
||||
} set;
|
||||
} dpaux = {6, 0x100, 0x10};
|
||||
|
||||
int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux);
|
||||
nvClose(fd);
|
||||
|
||||
if (rc == 0) {
|
||||
_getDockedHighestRefreshRate(0);
|
||||
g_canChangeRefreshRateDocked = true;
|
||||
} else {
|
||||
svcSleepThread(1000000000);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(internal) {
|
||||
*out_refreshRate = value;
|
||||
return true;
|
||||
}
|
||||
uint32_t fd = 0;
|
||||
if (!nvOpen(&fd, "/dev/nvdisp-disp1")) {
|
||||
NvdcMode2 display_b = {0};
|
||||
if (nvIoctl(fd, NVDISP_GET_MODE2, &display_b) == 0) {
|
||||
if (!display_b.pclkKHz) {
|
||||
nvClose(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_lastVActive != display_b.vActive) {
|
||||
g_lastVActive = display_b.vActive;
|
||||
_getDockedHighestRefreshRate(fd);
|
||||
}
|
||||
|
||||
uint64_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch;
|
||||
uint64_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch;
|
||||
uint64_t pixelClock = display_b.pclkKHz * 1000 + 999;
|
||||
value = (u32)(pixelClock / (h_total * v_total));
|
||||
} else {
|
||||
value = 60;
|
||||
}
|
||||
nvClose(fd);
|
||||
} else {
|
||||
value = 60;
|
||||
}
|
||||
}
|
||||
else if (!g_config.isRetroSUPER) {
|
||||
// Handheld mode
|
||||
g_config.isDocked = false;
|
||||
g_canChangeRefreshRateDocked = false;
|
||||
|
||||
uint32_t pixelClock = (9375ULL * ((4096 * ((2 * temp.PLLD_DIVN) + 1)) + misc.PLLD_SDM_DIN)) / (8 * temp.PLLD_DIVM);
|
||||
value = pixelClock / (DSI_CLOCK_HZ / 60);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*out_refreshRate = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown(void) {
|
||||
g_initialized = false;
|
||||
memset(&g_config, 0, sizeof(g_config));
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, based on reasearch by MasaGratoR and Cooler3D
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
namespace display {
|
||||
typedef struct {
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hSyncWidth;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vFrontPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint8_t vBackPorch;
|
||||
uint8_t VIC;
|
||||
uint32_t pixelClock_kHz;
|
||||
} DockedTimings;
|
||||
|
||||
typedef struct {
|
||||
uint8_t hSyncWidth;
|
||||
uint16_t hFrontPorch;
|
||||
uint8_t hBackPorch;
|
||||
uint8_t vSyncWidth;
|
||||
uint16_t vFrontPorch;
|
||||
uint8_t vBackPorch;
|
||||
uint32_t pixelClock_kHz;
|
||||
} HandheldTimings;
|
||||
|
||||
typedef struct {
|
||||
uint8_t min;
|
||||
uint8_t max;
|
||||
} MinMaxRefreshRate;
|
||||
|
||||
typedef struct {
|
||||
uint32_t unk0;
|
||||
uint32_t hActive;
|
||||
uint32_t vActive;
|
||||
uint32_t hSyncWidth;
|
||||
uint32_t vSyncWidth;
|
||||
uint32_t hFrontPorch;
|
||||
uint32_t vFrontPorch;
|
||||
uint32_t hBackPorch;
|
||||
uint32_t vBackPorch;
|
||||
uint32_t pclkKHz;
|
||||
uint32_t bitsPerPixel;
|
||||
uint32_t vmode;
|
||||
uint32_t sync;
|
||||
uint32_t unk1;
|
||||
uint32_t reserved;
|
||||
} NvdcMode2;
|
||||
|
||||
typedef struct {
|
||||
NvdcMode2 modes[201];
|
||||
uint32_t num_modes;
|
||||
} NvdcModeDB2;
|
||||
|
||||
typedef struct {
|
||||
unsigned int PLLD_DIVM: 8;
|
||||
unsigned int reserved_1: 3;
|
||||
unsigned int PLLD_DIVN: 8;
|
||||
unsigned int reserved_2: 1;
|
||||
unsigned int PLLD_DIVP: 3;
|
||||
unsigned int CSI_CLK_SRC: 1;
|
||||
unsigned int reserved_3: 1;
|
||||
unsigned int PLL_D: 1;
|
||||
unsigned int reserved_4: 1;
|
||||
unsigned int PLLD_LOCK: 1;
|
||||
unsigned int reserved_5: 1;
|
||||
unsigned int PLLD_REF_DIS: 1;
|
||||
unsigned int PLLD_ENABLE: 1;
|
||||
unsigned int PLLD_BYPASS: 1;
|
||||
} PLLD_BASE;
|
||||
|
||||
typedef struct {
|
||||
signed int PLLD_SDM_DIN: 16;
|
||||
unsigned int PLLD_EN_SDM: 1;
|
||||
unsigned int PLLD_LOCK_OVERRIDE: 1;
|
||||
unsigned int PLLD_EN_LCKDET: 1;
|
||||
unsigned int PLLD_FREQLOCK: 1;
|
||||
unsigned int PLLD_IDDQ: 1;
|
||||
unsigned int PLLD_ENABLE_CLK: 1;
|
||||
unsigned int PLLD_KVCO: 1;
|
||||
unsigned int PLLD_KCP: 2;
|
||||
unsigned int PLLD_PTS: 2;
|
||||
unsigned int PLLD_LDPULSE_ADJ: 3;
|
||||
unsigned int reserved: 2;
|
||||
} PLLD_MISC;
|
||||
|
||||
typedef struct {
|
||||
uint64_t clkVirtAddr;
|
||||
uint64_t dsiVirtAddr;
|
||||
bool isDocked;
|
||||
bool isLite;
|
||||
bool isRetroSUPER;
|
||||
bool isPossiblySpoofedRetro;
|
||||
bool dontForce60InDocked;
|
||||
bool matchLowestDocked;
|
||||
bool displaySync;
|
||||
bool displaySyncOutOfFocus60;
|
||||
bool displaySyncDocked;
|
||||
bool displaySyncDockedOutOfFocus60;
|
||||
} DisplayRefreshConfig;
|
||||
bool Initialize(const DisplayRefreshConfig* config);
|
||||
void SetDockedState(bool isDocked);
|
||||
bool SetRate(uint32_t new_refreshRate);
|
||||
bool GetRate(uint32_t* out_refreshRate, bool internal);
|
||||
uint8_t GetDockedHighestAllowed(void);
|
||||
void CorrectOledGamma(uint32_t refresh_rate);
|
||||
void SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p);
|
||||
void Shutdown(void);
|
||||
}
|
||||
@@ -1,647 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "clock_manager.hpp"
|
||||
#include <cstring>
|
||||
#include "file_utils.hpp"
|
||||
#include "board/board.hpp"
|
||||
#include "process_management.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "ipc_service.hpp"
|
||||
#include "kip.hpp"
|
||||
#include <i2c.h>
|
||||
#include "board/display_refresh_rate.hpp"
|
||||
#include <cstdio>
|
||||
#include <crc32.h>
|
||||
#include "config.hpp"
|
||||
#include "integrations.hpp"
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
#include "kip.hpp"
|
||||
#include "governor.hpp"
|
||||
|
||||
#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0))
|
||||
|
||||
namespace clockManager {
|
||||
|
||||
|
||||
bool gRunning = false;
|
||||
LockableMutex gContextMutex;
|
||||
HocClkContext gContext = {};
|
||||
FreqTable gFreqTable[HocClkModule_EnumMax];
|
||||
std::uint64_t gLastTempLogNs = 0;
|
||||
std::uint64_t gLastFreqLogNs = 0;
|
||||
std::uint64_t gLastPowerLogNs = 0;
|
||||
std::uint64_t gLastCsvWriteNs = 0;
|
||||
|
||||
bool IsAssignableHz(HocClkModule module, std::uint32_t hz)
|
||||
{
|
||||
switch (module) {
|
||||
case HocClkModule_CPU:
|
||||
return hz >= 500000000;
|
||||
case HocClkModule_MEM:
|
||||
return hz >= 665600000;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t GetMaxAllowedHz(HocClkModule module, HocClkProfile profile)
|
||||
{
|
||||
if (config::GetConfigValue(HocClkConfigValue_UncappedClocks)) {
|
||||
return ~0; // Integer limit, uncapped clocks ON
|
||||
} else {
|
||||
if (module == HocClkModule_GPU) {
|
||||
if (profile < HocClkProfile_HandheldCharging) {
|
||||
switch (board::GetSocType()) {
|
||||
case HocClkSocType_Erista:
|
||||
return 460800000;
|
||||
case HocClkSocType_Mariko:
|
||||
switch (config::GetConfigValue(KipConfigValue_marikoGpuUV)) {
|
||||
case 0:
|
||||
return 614400000;
|
||||
case 1:
|
||||
return 691200000;
|
||||
case 2:
|
||||
return 768000000;
|
||||
default:
|
||||
return 614400000;
|
||||
}
|
||||
default:
|
||||
return 460800000;
|
||||
}
|
||||
} else if (profile <= HocClkProfile_HandheldChargingUSB) {
|
||||
switch (board::GetSocType()) {
|
||||
case HocClkSocType_Erista:
|
||||
return 768000000;
|
||||
case HocClkSocType_Mariko:
|
||||
switch (config::GetConfigValue(KipConfigValue_marikoGpuUV)) {
|
||||
case 0:
|
||||
return 844800000;
|
||||
case 1:
|
||||
return 921600000;
|
||||
case 2:
|
||||
return 998400000;
|
||||
default:
|
||||
return 844800000;
|
||||
}
|
||||
default:
|
||||
return 768000000;
|
||||
}
|
||||
}
|
||||
} else if (module == HocClkModule_CPU) {
|
||||
if (profile < HocClkProfile_HandheldCharging && board::GetSocType() == HocClkSocType_Erista) {
|
||||
return 1581000000;
|
||||
} else {
|
||||
return ~0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t GetNearestHz(HocClkModule module, std::uint32_t inHz, std::uint32_t maxHz)
|
||||
{
|
||||
std::uint32_t *freqs = &gFreqTable[module].list[0];
|
||||
size_t count = gFreqTable[module].count - 1;
|
||||
|
||||
size_t i = 0;
|
||||
while (i < count) {
|
||||
if (maxHz > 0 && freqs[i] >= maxHz) {
|
||||
break;
|
||||
}
|
||||
if (inHz <= ((std::uint64_t)freqs[i] + freqs[i + 1]) / 2) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return freqs[i];
|
||||
}
|
||||
|
||||
void ResetToStockClocks()
|
||||
{
|
||||
board::ResetToStockCpu();
|
||||
if (config::GetConfigValue(HocClkConfigValue_LiveCpuUv)) {
|
||||
if (board::GetSocType() == HocClkSocType_Erista)
|
||||
board::SetDfllTunings(config::GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
|
||||
else
|
||||
board::SetDfllTunings(config::GetConfigValue(KipConfigValue_marikoCpuUVLow), config::GetConfigValue(KipConfigValue_marikoCpuUVHigh), board::CalculateTbreak(config::GetConfigValue(KipConfigValue_tableConf)));
|
||||
}
|
||||
|
||||
board::ResetToStockGpu();
|
||||
}
|
||||
|
||||
bool ConfigIntervalTimeout(HocClkConfigValue intervalMsConfigValue, std::uint64_t ns, std::uint64_t *lastLogNs)
|
||||
{
|
||||
std::uint64_t logInterval = config::GetConfigValue(intervalMsConfigValue) * 1000000ULL;
|
||||
bool shouldLog = logInterval && ((ns - *lastLogNs) > logInterval);
|
||||
|
||||
if (shouldLog) {
|
||||
*lastLogNs = ns;
|
||||
}
|
||||
|
||||
return shouldLog;
|
||||
}
|
||||
|
||||
void RefreshFreqTableRow(HocClkModule module)
|
||||
{
|
||||
std::scoped_lock lock{gContextMutex};
|
||||
|
||||
std::uint32_t freqs[HOCCLK_FREQ_LIST_MAX];
|
||||
std::uint32_t count;
|
||||
|
||||
fileUtils::LogLine("[mgr] %s freq list refresh", board::GetModuleName(module, true));
|
||||
board::GetFreqList(module, &freqs[0], HOCCLK_FREQ_LIST_MAX, &count);
|
||||
|
||||
std::uint32_t *hz = &gFreqTable[module].list[0];
|
||||
gFreqTable[module].count = 0;
|
||||
for (std::uint32_t i = 0; i < count; i++) {
|
||||
if (!IsAssignableHz(module, freqs[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*hz = freqs[i];
|
||||
fileUtils::LogLine("[mgr] %02u - %u - %u.%u MHz", gFreqTable[module].count, *hz, *hz / 1000000, *hz / 100000 - *hz / 1000000 * 10);
|
||||
|
||||
gFreqTable[module].count++;
|
||||
hz++;
|
||||
}
|
||||
|
||||
fileUtils::LogLine("[mgr] count = %u", gFreqTable[module].count);
|
||||
}
|
||||
|
||||
void HandleSafetyFeatures()
|
||||
{
|
||||
if (config::GetConfigValue(HocClkConfigValue_HandheldTDP) && (gContext.profile != HocClkProfile_Docked)) {
|
||||
if (board::GetConsoleType() == HocClkConsoleType_Hoag) {
|
||||
if (board::GetPowerMw(HocClkPowerSensor_Avg) < -(int)config::GetConfigValue(HocClkConfigValue_LiteTDPLimit)) {
|
||||
ResetToStockClocks();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (board::GetPowerMw(HocClkPowerSensor_Avg) < -(int)config::GetConfigValue(HocClkConfigValue_HandheldTDPLimit)) {
|
||||
ResetToStockClocks();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (((tmp451TempSoc() / 1000) > (int)config::GetConfigValue(HocClkConfigValue_ThermalThrottleThreshold)) && config::GetConfigValue(HocClkConfigValue_ThermalThrottle)) {
|
||||
ResetToStockClocks();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleMiscFeatures()
|
||||
{
|
||||
static u32 tick = 0;
|
||||
if(++tick > 10) {
|
||||
if (config::GetConfigValue(HocClkConfigValue_BatteryChargeCurrent)) {
|
||||
I2c_Bq24193_SetFastChargeCurrentLimit(config::GetConfigValue(HocClkConfigValue_BatteryChargeCurrent));
|
||||
}
|
||||
tick = 0;
|
||||
I2c_BuckConverter_SetMvOut(&I2c_Display, config::GetConfigValue(HocClkConfigValue_DisplayVoltage));
|
||||
}
|
||||
}
|
||||
|
||||
void DVFSBeforeSet(u32 targetHz)
|
||||
{
|
||||
s32 dvfsOffset = config::GetConfigValue(HocClkConfigValue_DVFSOffset);
|
||||
u32 vmin = board::GetMinimumGpuVmin(targetHz / 1000000, board::GetGpuSpeedoBracket()) + dvfsOffset;
|
||||
|
||||
board::PcvHijackGpuVolts(vmin);
|
||||
|
||||
/* Update the voltage. */
|
||||
if (I2c_BuckConverter_GetMvOut(&I2c_Mariko_GPU) < vmin) {
|
||||
I2c_BuckConverter_SetMvOut(&I2c_Mariko_GPU, vmin);
|
||||
}
|
||||
|
||||
gContext.voltages[HocClkVoltage_GPU] = vmin * 1000;
|
||||
}
|
||||
|
||||
void DVFSAfterSet(u32 targetHz)
|
||||
{
|
||||
s32 dvfsOffset = config::GetConfigValue(HocClkConfigValue_DVFSOffset);
|
||||
dvfsOffset = std::max(dvfsOffset, -80);
|
||||
u32 vmin = board::GetMinimumGpuVmin(targetHz / 1000000, board::GetGpuSpeedoBracket());
|
||||
|
||||
if (vmin) {
|
||||
vmin += dvfsOffset;
|
||||
}
|
||||
|
||||
u32 maxHz = GetMaxAllowedHz(HocClkModule_GPU, gContext.profile);
|
||||
u32 nearestHz = GetNearestHz(HocClkModule_GPU, targetHz, maxHz);
|
||||
board::PcvHijackGpuVolts(vmin);
|
||||
|
||||
if (targetHz) {
|
||||
board::SetHz(HocClkModule_GPU, ~0);
|
||||
board::SetHz(HocClkModule_GPU, nearestHz);
|
||||
} else {
|
||||
board::SetHz(HocClkModule_GPU, ~0);
|
||||
board::ResetToStockGpu();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleCpuUv()
|
||||
{
|
||||
if (board::GetSocType() == HocClkSocType_Erista)
|
||||
board::SetDfllTunings(config::GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
|
||||
else
|
||||
board::SetDfllTunings(config::GetConfigValue(KipConfigValue_marikoCpuUVLow), config::GetConfigValue(KipConfigValue_marikoCpuUVHigh), board::CalculateTbreak(config::GetConfigValue(KipConfigValue_tableConf)));
|
||||
}
|
||||
|
||||
void DVFSReset()
|
||||
{
|
||||
if (board::GetSocType() == HocClkSocType_Mariko && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
|
||||
board::PcvHijackGpuVolts(0);
|
||||
|
||||
u32 targetHz = gContext.overrideFreqs[HocClkModule_GPU];
|
||||
if (!targetHz) {
|
||||
targetHz = config::GetAutoClockHz(gContext.applicationId, HocClkModule_GPU, gContext.profile, false);
|
||||
if (!targetHz) {
|
||||
targetHz = config::GetAutoClockHz(GLOBAL_PROFILE_ID, HocClkModule_GPU, gContext.profile, false);
|
||||
}
|
||||
}
|
||||
u32 maxHz = GetMaxAllowedHz(HocClkModule_GPU, gContext.profile);
|
||||
u32 nearestHz = GetNearestHz(HocClkModule_GPU, targetHz, maxHz);
|
||||
|
||||
board::SetHz(HocClkModule_GPU, ~0);
|
||||
if (targetHz) {
|
||||
board::SetHz(HocClkModule_GPU, nearestHz);
|
||||
} else {
|
||||
board::ResetToStockGpu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleFreqReset(HocClkModule module, bool isBoost)
|
||||
{
|
||||
switch (module) {
|
||||
case HocClkModule_CPU:
|
||||
if (!(isBoost || (config::GetConfigValue(HocClkConfigValue_OverwriteBoostMode) && isBoost)))
|
||||
board::ResetToStockCpu();
|
||||
if (config::GetConfigValue(HocClkConfigValue_LiveCpuUv)) {
|
||||
if (board::GetSocType() == HocClkSocType_Erista)
|
||||
board::SetDfllTunings(config::GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
|
||||
else
|
||||
board::SetDfllTunings(config::GetConfigValue(KipConfigValue_marikoCpuUVLow), config::GetConfigValue(KipConfigValue_marikoCpuUVHigh), board::CalculateTbreak(config::GetConfigValue(KipConfigValue_tableConf)));
|
||||
}
|
||||
break;
|
||||
case HocClkModule_GPU:
|
||||
board::ResetToStockGpu();
|
||||
break;
|
||||
case HocClkModule_MEM:
|
||||
board::ResetToStockMem();
|
||||
DVFSReset();
|
||||
break;
|
||||
case HocClkModule_Display:
|
||||
if (config::GetConfigValue(HocClkConfigValue_OverwriteRefreshRate)) {
|
||||
board::ResetToStockDisplay();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetClocks(bool isBoost)
|
||||
{
|
||||
std::uint32_t targetHz = 0;
|
||||
std::uint32_t maxHz = 0;
|
||||
std::uint32_t nearestHz = 0;
|
||||
|
||||
if (isBoost && !config::GetConfigValue(HocClkConfigValue_OverwriteBoostMode)) {
|
||||
u32 boostFreq = board::GetHz(HocClkModule_CPU);
|
||||
if (boostFreq / 1000000 > 1785) {
|
||||
board::SetHz(HocClkModule_CPU, boostFreq);
|
||||
}
|
||||
return; // Return if we aren't overwriting boost mode
|
||||
}
|
||||
|
||||
bool returnRaw = false; // Return a value scaled to MHz instead of raw value
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
u32 oldHz = board::GetHz((HocClkModule)module); // Get Old hz (used primarily for DVFS Logic)
|
||||
|
||||
if (module > HocClkModule_MEM)
|
||||
returnRaw = true;
|
||||
else
|
||||
returnRaw = false;
|
||||
targetHz = gContext.overrideFreqs[module];
|
||||
if (!targetHz) {
|
||||
targetHz = config::GetAutoClockHz(gContext.applicationId, (HocClkModule)module, gContext.profile, returnRaw);
|
||||
if (!targetHz)
|
||||
targetHz = config::GetAutoClockHz(GLOBAL_PROFILE_ID, (HocClkModule)module, gContext.profile, returnRaw);
|
||||
}
|
||||
|
||||
if (module == HocClkModule_Governor) {
|
||||
governor::HandleGovernor(targetHz);
|
||||
}
|
||||
|
||||
bool noCPU = governor::isCpuGovernorEnabled;
|
||||
bool noGPU = governor::isGpuGovernorEnabled;
|
||||
bool noDisp = governor::isVRREnabled;
|
||||
if (noDisp && module == HocClkModule_Display)
|
||||
continue;
|
||||
|
||||
if (module == HocClkModule_Display && config::GetConfigValue(HocClkConfigValue_OverwriteRefreshRate) && !noDisp) {
|
||||
if (targetHz) {
|
||||
board::SetHz(HocClkModule_Display, targetHz);
|
||||
gContext.freqs[HocClkModule_Display] = targetHz;
|
||||
gContext.realFreqs[HocClkModule_Display] = targetHz;
|
||||
} else {
|
||||
HandleFreqReset(HocClkModule_Display, isBoost);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip GPU and CPU if governors handle them
|
||||
if (module > HocClkModule_MEM) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (noCPU && module == HocClkModule_CPU)
|
||||
continue;
|
||||
if (noGPU && module == HocClkModule_GPU)
|
||||
continue;
|
||||
|
||||
if (targetHz) {
|
||||
maxHz = GetMaxAllowedHz((HocClkModule)module, gContext.profile);
|
||||
nearestHz = GetNearestHz((HocClkModule)module, targetHz, maxHz);
|
||||
|
||||
if (nearestHz != gContext.freqs[module]) {
|
||||
fileUtils::LogLine(
|
||||
"[mgr] %s clock set : %u.%u MHz (target = %u.%u MHz)",
|
||||
board::GetModuleName((HocClkModule)module, true),
|
||||
nearestHz / 1000000, nearestHz / 100000 - nearestHz / 1000000 * 10,
|
||||
targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10
|
||||
);
|
||||
|
||||
if (module == HocClkModule_MEM && board::GetSocType() == HocClkSocType_Mariko && targetHz > oldHz && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
|
||||
DVFSBeforeSet(targetHz);
|
||||
}
|
||||
|
||||
board::SetHz((HocClkModule)module, nearestHz);
|
||||
gContext.freqs[module] = nearestHz;
|
||||
|
||||
if (module == HocClkModule_CPU && config::GetConfigValue(HocClkConfigValue_LiveCpuUv)) {
|
||||
HandleCpuUv();
|
||||
}
|
||||
|
||||
if (module == HocClkModule_MEM && board::GetSocType() == HocClkSocType_Mariko && targetHz < oldHz && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
|
||||
DVFSAfterSet(targetHz);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HandleFreqReset((HocClkModule)module, isBoost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RefreshContext()
|
||||
{
|
||||
bool hasChanged = false;
|
||||
|
||||
std::uint32_t mode = 0;
|
||||
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
|
||||
std::uint64_t applicationId = processManagement::GetCurrentApplicationId();
|
||||
if (applicationId != gContext.applicationId) {
|
||||
fileUtils::LogLine("[mgr] TitleID change: %016lX", applicationId);
|
||||
gContext.applicationId = applicationId;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
HocClkProfile profile = board::GetProfile();
|
||||
if (profile != gContext.profile) {
|
||||
fileUtils::LogLine("[mgr] Profile change: %s", board::GetProfileName(profile, true));
|
||||
gContext.profile = profile;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
// restore clocks to stock values on app or profile change
|
||||
if (hasChanged) {
|
||||
board::ResetToStock();
|
||||
if (board::GetSocType() == HocClkSocType_Mariko && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
|
||||
board::PcvHijackGpuVolts(0);
|
||||
board::SetHz(HocClkModule_GPU, ~0);
|
||||
board::ResetToStockGpu();
|
||||
}
|
||||
WaitForNextTick();
|
||||
}
|
||||
|
||||
std::uint32_t hz = 0;
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
hz = board::GetHz((HocClkModule)module);
|
||||
if (hz != 0 && hz != gContext.freqs[module]) {
|
||||
fileUtils::LogLine("[mgr] %s clock change: %u.%u MHz", board::GetModuleName((HocClkModule)module, true), hz / 1000000, hz / 100000 - hz / 1000000 * 10);
|
||||
gContext.freqs[module] = hz;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
hz = config::GetOverrideHz((HocClkModule)module);
|
||||
if (hz != gContext.overrideFreqs[module]) {
|
||||
if (hz) {
|
||||
fileUtils::LogLine("[mgr] %s override change: %u.%u MHz", board::GetModuleName((HocClkModule)module, true), hz / 1000000, hz / 100000 - hz / 1000000 * 10);
|
||||
}
|
||||
gContext.overrideFreqs[module] = hz;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::uint64_t ns = armTicksToNs(armGetSystemTick());
|
||||
|
||||
// temperatures do not and should not force a refresh, hasChanged untouched
|
||||
std::uint32_t millis = 0;
|
||||
bool shouldLogTemp = ConfigIntervalTimeout(HocClkConfigValue_TempLogIntervalMs, ns, &gLastTempLogNs);
|
||||
for (unsigned int sensor = 0; sensor < HocClkThermalSensor_EnumMax; sensor++) {
|
||||
millis = board::GetTemperatureMilli((HocClkThermalSensor)sensor);
|
||||
if (shouldLogTemp) {
|
||||
fileUtils::LogLine("[mgr] %s temp: %u.%u °C", board::GetThermalSensorName((HocClkThermalSensor)sensor, true), millis / 1000, (millis - millis / 1000 * 1000) / 100);
|
||||
}
|
||||
gContext.temps[sensor] = millis;
|
||||
}
|
||||
|
||||
// power stats do not and should not force a refresh, hasChanged untouched
|
||||
std::int32_t mw = 0;
|
||||
bool shouldLogPower = ConfigIntervalTimeout(HocClkConfigValue_PowerLogIntervalMs, ns, &gLastPowerLogNs);
|
||||
for (unsigned int sensor = 0; sensor < HocClkPowerSensor_EnumMax; sensor++) {
|
||||
mw = board::GetPowerMw((HocClkPowerSensor)sensor);
|
||||
if (shouldLogPower) {
|
||||
fileUtils::LogLine("[mgr] Power %s: %d mW", board::GetPowerSensorName((HocClkPowerSensor)sensor, false), mw);
|
||||
}
|
||||
gContext.power[sensor] = mw;
|
||||
}
|
||||
|
||||
// real freqs do not and should not force a refresh, hasChanged untouched
|
||||
std::uint32_t realHz = 0;
|
||||
bool shouldLogFreq = ConfigIntervalTimeout(HocClkConfigValue_FreqLogIntervalMs, ns, &gLastFreqLogNs);
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
realHz = board::GetRealHz((HocClkModule)module);
|
||||
if (shouldLogFreq) {
|
||||
fileUtils::LogLine("[mgr] %s real freq: %u.%u MHz", board::GetModuleName((HocClkModule)module, true), realHz / 1000000, realHz / 100000 - realHz / 1000000 * 10);
|
||||
}
|
||||
gContext.realFreqs[module] = realHz;
|
||||
}
|
||||
|
||||
// ram load do not and should not force a refresh, hasChanged untouched
|
||||
for (unsigned int loadSource = 0; loadSource < HocClkPartLoad_EnumMax; loadSource++) {
|
||||
gContext.partLoad[loadSource] = board::GetPartLoad((HocClkPartLoad)loadSource);
|
||||
}
|
||||
|
||||
for (unsigned int voltageSource = 0; voltageSource < HocClkVoltage_EnumMax; voltageSource++) {
|
||||
gContext.voltages[voltageSource] = board::GetVoltage((HocClkVoltage)voltageSource);
|
||||
}
|
||||
|
||||
if (ConfigIntervalTimeout(HocClkConfigValue_CsvWriteIntervalMs, ns, &gLastCsvWriteNs)) {
|
||||
fileUtils::WriteContextToCsv(&gContext);
|
||||
}
|
||||
|
||||
// this->context->maxDisplayFreq = board::GetHighestDockedDisplayRate();
|
||||
u32 targetHz = gContext.overrideFreqs[HocClkModule_Display];
|
||||
if (!targetHz) {
|
||||
targetHz = config::GetAutoClockHz(gContext.applicationId, HocClkModule_Display, gContext.profile, true);
|
||||
if (!targetHz)
|
||||
targetHz = config::GetAutoClockHz(GLOBAL_PROFILE_ID, HocClkModule_Display, gContext.profile, true);
|
||||
}
|
||||
|
||||
if (board::GetConsoleType() != HocClkConsoleType_Hoag)
|
||||
board::SetDisplayRefreshDockedState(gContext.profile == HocClkProfile_Docked);
|
||||
|
||||
if (gContext.isSaltyNXInstalled)
|
||||
gContext.fps = integrations::GetSaltyNXFPS();
|
||||
else
|
||||
gContext.fps = 254; // N/A
|
||||
|
||||
if (gContext.isSaltyNXInstalled)
|
||||
gContext.resolutionHeight = integrations::GetSaltyNXResolutionHeight();
|
||||
else
|
||||
gContext.resolutionHeight = 0; // N/A
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
config::Initialize();
|
||||
|
||||
gContext = {};
|
||||
gContext.applicationId = 0;
|
||||
gContext.profile = HocClkProfile_Handheld;
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
gContext.freqs[module] = 0;
|
||||
gContext.realFreqs[module] = 0;
|
||||
gContext.overrideFreqs[module] = 0;
|
||||
RefreshFreqTableRow((HocClkModule)module);
|
||||
}
|
||||
|
||||
gRunning = false;
|
||||
gLastTempLogNs = 0;
|
||||
gLastCsvWriteNs = 0;
|
||||
|
||||
kip::GetKipData();
|
||||
|
||||
board::FuseData *fuse = board::GetFuseData();
|
||||
|
||||
gContext.speedos[HocClkSpeedo_CPU] = fuse->cpuSpeedo;
|
||||
gContext.speedos[HocClkSpeedo_GPU] = fuse->gpuSpeedo;
|
||||
gContext.speedos[HocClkSpeedo_SOC] = fuse->socSpeedo;
|
||||
gContext.iddq[HocClkSpeedo_CPU] = fuse->cpuIDDQ;
|
||||
gContext.iddq[HocClkSpeedo_GPU] = fuse->gpuIDDQ;
|
||||
gContext.iddq[HocClkSpeedo_SOC] = fuse->socIDDQ;
|
||||
gContext.waferX = fuse->waferX;
|
||||
gContext.waferY = fuse->waferY;
|
||||
|
||||
gContext.dramID = board::GetDramID();
|
||||
gContext.isDram8GB = board::IsDram8GB();
|
||||
board::SetGpuSchedulingMode((GpuSchedulingMode)config::GetConfigValue(HocClkConfigValue_GPUScheduling), (GpuSchedulingOverrideMethod)config::GetConfigValue(HocClkConfigValue_GPUSchedulingMethod));
|
||||
gContext.gpuSchedulingMode = (GpuSchedulingMode)config::GetConfigValue(HocClkConfigValue_GPUScheduling);
|
||||
|
||||
gContext.isSysDockInstalled = integrations::GetSysDockState();
|
||||
gContext.isSaltyNXInstalled = integrations::GetSaltyNXState();
|
||||
if (gContext.isSaltyNXInstalled) {
|
||||
integrations::LoadSaltyNX();
|
||||
}
|
||||
|
||||
gContext.isUsingRetroSuper = integrations::GetRETROSuperStatus();
|
||||
governor::startThreads();
|
||||
}
|
||||
|
||||
void Exit()
|
||||
{
|
||||
governor::exitThreads();
|
||||
config::Exit();
|
||||
}
|
||||
|
||||
HocClkContext GetCurrentContext()
|
||||
{
|
||||
std::scoped_lock lock{gContextMutex};
|
||||
return gContext;
|
||||
}
|
||||
|
||||
void SetRunning(bool running)
|
||||
{
|
||||
gRunning = running;
|
||||
}
|
||||
|
||||
bool Running()
|
||||
{
|
||||
return gRunning;
|
||||
}
|
||||
|
||||
void GetFreqList(HocClkModule module, std::uint32_t *list, std::uint32_t maxCount, std::uint32_t *outCount)
|
||||
{
|
||||
ASSERT_ENUM_VALID(HocClkModule, module);
|
||||
|
||||
*outCount = std::min(maxCount, gFreqTable[module].count);
|
||||
memcpy(list, &gFreqTable[module].list[0], *outCount * sizeof(gFreqTable[0].list[0]));
|
||||
}
|
||||
|
||||
void Tick()
|
||||
{
|
||||
fileUtils::LogLine("CPU Temp: %d", board::GetTemperatureMilli(HocClkThermalSensor_CPU));
|
||||
std::scoped_lock lock{gContextMutex};
|
||||
std::uint32_t mode = 0;
|
||||
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
|
||||
bool isBoost = apmExtIsBoostMode(mode);
|
||||
|
||||
HandleSafetyFeatures();
|
||||
HandleMiscFeatures();
|
||||
|
||||
if (RefreshContext() || config::Refresh()) {
|
||||
SetClocks(isBoost);
|
||||
}
|
||||
}
|
||||
|
||||
void WaitForNextTick()
|
||||
{
|
||||
if (board::GetHz(HocClkModule_MEM) > 665000000)
|
||||
svcSleepThread(config::GetConfigValue(HocClkConfigValue_PollingIntervalMs) * 1000000ULL);
|
||||
else
|
||||
svcSleepThread(5000 * 1000000ULL); // 5 seconds in sleep mode
|
||||
}
|
||||
} // namespace clockManager
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <hocclk.h>
|
||||
#include <switch.h>
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
|
||||
namespace clockManager {
|
||||
|
||||
struct FreqTable {
|
||||
std::uint32_t count;
|
||||
std::uint32_t list[HOCCLK_FREQ_LIST_MAX];
|
||||
};
|
||||
|
||||
|
||||
extern bool hasChanged;
|
||||
|
||||
// instance variables
|
||||
extern bool gRunning;
|
||||
extern LockableMutex gContextMutex;
|
||||
extern HocClkContext gContext;
|
||||
extern FreqTable gFreqTable[HocClkModule_EnumMax];
|
||||
extern std::uint64_t gLastTempLogNs;
|
||||
extern std::uint64_t gLastFreqLogNs;
|
||||
extern std::uint64_t gLastPowerLogNs;
|
||||
extern std::uint64_t gLastCsvWriteNs;
|
||||
|
||||
|
||||
void Initialize();
|
||||
void Exit();
|
||||
|
||||
HocClkContext GetCurrentContext();
|
||||
|
||||
void SetRunning(bool running);
|
||||
bool Running();
|
||||
|
||||
std::uint32_t GetMaxAllowedHz(HocClkModule module, HocClkProfile profile);
|
||||
bool IsAssignableHz(HocClkModule module, std::uint32_t hz);
|
||||
|
||||
void GetFreqList(HocClkModule module, std::uint32_t* list, std::uint32_t maxCount, std::uint32_t* outCount);
|
||||
|
||||
void Tick();
|
||||
void WaitForNextTick();
|
||||
}
|
||||
@@ -1,477 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "config.hpp"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <initializer_list>
|
||||
#include <minIni.h>
|
||||
#include <nxExt.h>
|
||||
#include "board/board.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "file_utils.hpp"
|
||||
|
||||
namespace config {
|
||||
|
||||
uint64_t configValues[HocClkConfigValue_EnumMax];
|
||||
|
||||
namespace {
|
||||
|
||||
bool gLoaded = false;
|
||||
std::string gPath;
|
||||
time_t gMtime = 0;
|
||||
std::atomic_bool gEnabled{false};
|
||||
std::uint32_t gOverrideFreqs[HocClkModule_EnumMax];
|
||||
std::map<std::tuple<std::uint64_t, HocClkProfile, HocClkModule>, std::uint32_t> gProfileMHzMap;
|
||||
std::map<std::uint64_t, std::uint8_t> gProfileCountMap;
|
||||
LockableMutex gConfigMutex;
|
||||
LockableMutex gOverrideMutex;
|
||||
|
||||
time_t CheckModificationTime() {
|
||||
time_t mtime = 0;
|
||||
struct stat st;
|
||||
if (stat(gPath.c_str(), &st) == 0) {
|
||||
mtime = st.st_mtime;
|
||||
}
|
||||
return mtime;
|
||||
}
|
||||
|
||||
std::uint32_t FindClockMHz(std::uint64_t tid, HocClkModule module, HocClkProfile profile) {
|
||||
if (gLoaded) {
|
||||
auto it = gProfileMHzMap.find(std::make_tuple(tid, profile, module));
|
||||
if (it != gProfileMHzMap.end()) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::uint32_t FindClockHzFromProfiles(std::uint64_t tid, HocClkModule module, std::initializer_list<HocClkProfile> profiles, u32 mhzMultiplier = 1000000) {
|
||||
std::uint32_t mhz = 0;
|
||||
|
||||
if (gLoaded) {
|
||||
for (auto profile: profiles) {
|
||||
mhz = FindClockMHz(tid, module, profile);
|
||||
if (mhz) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::max((std::uint32_t)0, mhz * mhzMultiplier);
|
||||
}
|
||||
|
||||
int BrowseIniFunc(const char* section, const char* key, const char* value, void* userdata) {
|
||||
(void)userdata;
|
||||
std::uint64_t input;
|
||||
if (!strcmp(section, CONFIG_VAL_SECTION)) {
|
||||
for (unsigned int kval = 0; kval < HocClkConfigValue_EnumMax; kval++) {
|
||||
if (!strcmp(key, hocclkFormatConfigValue((HocClkConfigValue)kval, false))) {
|
||||
input = strtoul(value, NULL, 0);
|
||||
if (!hocclkValidConfigValue((HocClkConfigValue)kval, input)) {
|
||||
input = hocclkDefaultConfigValue((HocClkConfigValue)kval);
|
||||
fileUtils::LogLine("[cfg] Invalid value for key '%s' in section '%s': using default %d", key, section, input);
|
||||
}
|
||||
configValues[kval] = input;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
fileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized config value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint64_t tid = strtoul(section, NULL, 16);
|
||||
|
||||
if (!tid || strlen(section) != 16) {
|
||||
fileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid TitleID", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
HocClkProfile parsedProfile = HocClkProfile_EnumMax;
|
||||
HocClkModule parsedModule = HocClkModule_EnumMax;
|
||||
|
||||
for (unsigned int profile = 0; profile < HocClkProfile_EnumMax; profile++) {
|
||||
const char* profileCode = board::GetProfileName((HocClkProfile)profile, false);
|
||||
size_t profileCodeLen = strlen(profileCode);
|
||||
|
||||
if (!strncmp(key, profileCode, profileCodeLen) && key[profileCodeLen] == '_') {
|
||||
const char* subkey = key + profileCodeLen + 1;
|
||||
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
const char* moduleCode = board::GetModuleName((HocClkModule)module, false);
|
||||
size_t moduleCodeLen = strlen(moduleCode);
|
||||
if (!strncmp(subkey, moduleCode, moduleCodeLen) && subkey[moduleCodeLen] == '\0') {
|
||||
parsedProfile = (HocClkProfile)profile;
|
||||
parsedModule = (HocClkModule)module;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedModule == HocClkModule_EnumMax || parsedProfile == HocClkProfile_EnumMax) {
|
||||
fileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized key", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::uint32_t mhz = strtoul(value, NULL, 10);
|
||||
if (!mhz) {
|
||||
fileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid value", key, section);
|
||||
return 1;
|
||||
}
|
||||
|
||||
gProfileMHzMap[std::make_tuple(tid, parsedProfile, parsedModule)] = mhz;
|
||||
auto it = gProfileCountMap.find(tid);
|
||||
if (it == gProfileCountMap.end()) {
|
||||
gProfileCountMap[tid] = 1;
|
||||
} else {
|
||||
it->second++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Close() {
|
||||
gLoaded = false;
|
||||
gProfileMHzMap.clear();
|
||||
gProfileCountMap.clear();
|
||||
for (unsigned int i = 0; i < HocClkConfigValue_EnumMax; i++) {
|
||||
configValues[i] = hocclkDefaultConfigValue((HocClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
|
||||
void Load() {
|
||||
fileUtils::LogLine("[cfg] Reading %s", gPath.c_str());
|
||||
|
||||
Close();
|
||||
gMtime = CheckModificationTime();
|
||||
if (!gMtime) {
|
||||
fileUtils::LogLine("[cfg] Error finding file");
|
||||
} else if (!ini_browse(&BrowseIniFunc, nullptr, gPath.c_str())) {
|
||||
fileUtils::LogLine("[cfg] Error loading file");
|
||||
}
|
||||
|
||||
gLoaded = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
gPath = FILE_CONFIG_DIR "/config.ini";
|
||||
gLoaded = false;
|
||||
gProfileMHzMap.clear();
|
||||
gProfileCountMap.clear();
|
||||
gMtime = 0;
|
||||
gEnabled = false;
|
||||
for (unsigned int i = 0; i < HocClkModule_EnumMax; i++) {
|
||||
gOverrideFreqs[i] = 0;
|
||||
}
|
||||
for (unsigned int i = 0; i < HocClkConfigValue_EnumMax; i++) {
|
||||
configValues[i] = hocclkDefaultConfigValue((HocClkConfigValue)i);
|
||||
}
|
||||
}
|
||||
|
||||
void Exit() {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
Close();
|
||||
}
|
||||
|
||||
bool Refresh() {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
if (!gLoaded || gMtime != CheckModificationTime()) {
|
||||
Load();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasProfilesLoaded() {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
return gLoaded;
|
||||
}
|
||||
|
||||
std::uint32_t GetAutoClockHz(std::uint64_t tid, HocClkModule module, HocClkProfile profile, bool returnRaw) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
switch (profile) {
|
||||
case HocClkProfile_Handheld:
|
||||
return FindClockHzFromProfiles(tid, module, {HocClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case HocClkProfile_HandheldCharging:
|
||||
case HocClkProfile_HandheldChargingUSB:
|
||||
return FindClockHzFromProfiles(tid, module, {HocClkProfile_HandheldChargingUSB, HocClkProfile_HandheldCharging, HocClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case HocClkProfile_HandheldChargingOfficial:
|
||||
return FindClockHzFromProfiles(tid, module, {HocClkProfile_HandheldChargingOfficial, HocClkProfile_HandheldCharging, HocClkProfile_Handheld}, returnRaw ? 1 : 1000000);
|
||||
case HocClkProfile_Docked:
|
||||
return FindClockHzFromProfiles(tid, module, {HocClkProfile_Docked}, returnRaw ? 1 : 1000000);
|
||||
default:
|
||||
ERROR_THROW("Unhandled HocClkProfile: %u", profile);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GetProfiles(std::uint64_t tid, HocClkTitleProfileList* out_profiles) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
for (unsigned int profile = 0; profile < HocClkProfile_EnumMax; profile++) {
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
out_profiles->mhzMap[profile][module] = FindClockMHz(tid, (HocClkModule)module, (HocClkProfile)profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SetProfiles(std::uint64_t tid, HocClkTitleProfileList* profiles, bool immediate) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
uint8_t numProfiles = 0;
|
||||
|
||||
char section[17] = {0};
|
||||
snprintf(section, sizeof(section), "%016lX", tid);
|
||||
|
||||
std::vector<std::string> keys;
|
||||
std::vector<std::string> values;
|
||||
keys.reserve(+HocClkProfile_EnumMax * +HocClkModule_EnumMax);
|
||||
values.reserve(+HocClkProfile_EnumMax * +HocClkModule_EnumMax);
|
||||
|
||||
std::uint32_t* mhz = &profiles->mhz[0];
|
||||
|
||||
for (unsigned int profile = 0; profile < HocClkProfile_EnumMax; profile++) {
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
if (*mhz) {
|
||||
numProfiles++;
|
||||
|
||||
std::string key = std::string(board::GetProfileName((HocClkProfile)profile, false)) +
|
||||
"_" +
|
||||
board::GetModuleName((HocClkModule)module, false);
|
||||
std::string value = std::to_string(*mhz);
|
||||
|
||||
keys.push_back(key);
|
||||
values.push_back(value);
|
||||
}
|
||||
mhz++;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<const char*> keyPointers;
|
||||
std::vector<const char*> valuePointers;
|
||||
keyPointers.reserve(keys.size() + 1);
|
||||
valuePointers.reserve(values.size() + 1);
|
||||
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
keyPointers.push_back(keys[i].c_str());
|
||||
valuePointers.push_back(values[i].c_str());
|
||||
}
|
||||
keyPointers.push_back(NULL);
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if (!ini_putsection(section, keyPointers.data(), valuePointers.data(), gPath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
mhz = &profiles->mhz[0];
|
||||
gProfileCountMap[tid] = numProfiles;
|
||||
for (unsigned int profile = 0; profile < HocClkProfile_EnumMax; profile++) {
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
if (*mhz) {
|
||||
gProfileMHzMap[std::make_tuple(tid, (HocClkProfile)profile, (HocClkModule)module)] = *mhz;
|
||||
} else {
|
||||
gProfileMHzMap.erase(std::make_tuple(tid, (HocClkProfile)profile, (HocClkModule)module));
|
||||
}
|
||||
mhz++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::uint8_t GetProfileCount(std::uint64_t tid) {
|
||||
auto it = gProfileCountMap.find(tid);
|
||||
if (it == gProfileCountMap.end()) {
|
||||
return 0;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void SetEnabled(bool enabled) {
|
||||
gEnabled = enabled;
|
||||
}
|
||||
|
||||
bool Enabled() {
|
||||
return gEnabled;
|
||||
}
|
||||
|
||||
void SetOverrideHz(HocClkModule module, std::uint32_t hz) {
|
||||
ASSERT_ENUM_VALID(HocClkModule, module);
|
||||
std::scoped_lock lock{gOverrideMutex};
|
||||
gOverrideFreqs[module] = hz;
|
||||
}
|
||||
|
||||
std::uint32_t GetOverrideHz(HocClkModule module) {
|
||||
ASSERT_ENUM_VALID(HocClkModule, module);
|
||||
std::scoped_lock lock{gOverrideMutex};
|
||||
return gOverrideFreqs[module];
|
||||
}
|
||||
|
||||
std::uint64_t GetConfigValue(HocClkConfigValue kval) {
|
||||
ASSERT_ENUM_VALID(HocClkConfigValue, kval);
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
return configValues[kval];
|
||||
}
|
||||
|
||||
const char* GetConfigValueName(HocClkConfigValue kval, bool pretty) {
|
||||
ASSERT_ENUM_VALID(HocClkConfigValue, kval);
|
||||
return hocclkFormatConfigValue(kval, pretty);
|
||||
}
|
||||
|
||||
void GetConfigValues(HocClkConfigValueList* out_configValues) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
for (unsigned int kval = 0; kval < HocClkConfigValue_EnumMax; kval++) {
|
||||
out_configValues->values[kval] = configValues[kval];
|
||||
}
|
||||
}
|
||||
|
||||
bool SetConfigValues(HocClkConfigValueList* configValues, bool immediate) {
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
|
||||
std::vector<const char*> iniKeys;
|
||||
std::vector<std::string> iniValues;
|
||||
iniKeys.reserve(HocClkConfigValue_EnumMax + 1);
|
||||
iniValues.reserve(HocClkConfigValue_EnumMax);
|
||||
|
||||
for (unsigned int kval = 0; kval < HocClkConfigValue_EnumMax; kval++) {
|
||||
if (!hocclkValidConfigValue((HocClkConfigValue)kval, configValues->values[kval]) ||
|
||||
configValues->values[kval] == hocclkDefaultConfigValue((HocClkConfigValue)kval)) {
|
||||
continue;
|
||||
}
|
||||
iniValues.push_back(std::to_string(configValues->values[kval]));
|
||||
iniKeys.push_back(hocclkFormatConfigValue((HocClkConfigValue)kval, false));
|
||||
}
|
||||
|
||||
iniKeys.push_back(NULL);
|
||||
|
||||
std::vector<const char*> valuePointers;
|
||||
valuePointers.reserve(iniValues.size() + 1);
|
||||
for (const auto& val : iniValues) {
|
||||
valuePointers.push_back(val.c_str());
|
||||
}
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if (!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), gPath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
for (unsigned int kval = 0; kval < HocClkConfigValue_EnumMax; kval++) {
|
||||
if (hocclkValidConfigValue((HocClkConfigValue)kval, configValues->values[kval])) {
|
||||
config::configValues[kval] = configValues->values[kval];
|
||||
} else {
|
||||
config::configValues[kval] = hocclkDefaultConfigValue((HocClkConfigValue)kval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResetConfigValue(HocClkConfigValue kval) {
|
||||
if (!HOCCLK_ENUM_VALID(HocClkConfigValue, kval)) {
|
||||
fileUtils::LogLine("[cfg] Invalid HocClkConfigValue: %u", kval);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
|
||||
std::uint64_t defaultValue = hocclkDefaultConfigValue(kval);
|
||||
|
||||
std::vector<const char*> iniKeys;
|
||||
std::vector<std::string> iniValues;
|
||||
iniKeys.reserve(2);
|
||||
iniValues.reserve(1);
|
||||
|
||||
iniKeys.push_back(hocclkFormatConfigValue(kval, false));
|
||||
iniValues.push_back("");
|
||||
iniKeys.push_back(NULL);
|
||||
|
||||
std::vector<const char*> valuePointers;
|
||||
valuePointers.reserve(iniValues.size() + 1);
|
||||
for (const auto& val : iniValues) {
|
||||
valuePointers.push_back(val.c_str());
|
||||
}
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if (!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), gPath.c_str())) {
|
||||
fileUtils::LogLine("[cfg] Failed to reset config value %u in INI", kval);
|
||||
return false;
|
||||
}
|
||||
|
||||
configValues[kval] = defaultValue;
|
||||
fileUtils::LogLine("[cfg] Reset config value %u to default: %llu", kval, defaultValue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetConfigValue(HocClkConfigValue kval, std::uint64_t value, bool immediate) {
|
||||
if (!HOCCLK_ENUM_VALID(HocClkConfigValue, kval)) {
|
||||
return false;
|
||||
}
|
||||
if (!hocclkValidConfigValue(kval, value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{gConfigMutex};
|
||||
|
||||
std::vector<const char*> iniKeys;
|
||||
std::vector<std::string> iniValues;
|
||||
iniKeys.reserve(2);
|
||||
iniValues.reserve(1);
|
||||
|
||||
iniKeys.push_back(hocclkFormatConfigValue(kval, false));
|
||||
iniValues.push_back(std::to_string(value));
|
||||
iniKeys.push_back(NULL);
|
||||
|
||||
std::vector<const char*> valuePointers;
|
||||
valuePointers.reserve(2);
|
||||
valuePointers.push_back(iniValues[0].c_str());
|
||||
valuePointers.push_back(NULL);
|
||||
|
||||
if (!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), gPath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (immediate) {
|
||||
configValues[kval] = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <hocclk.h>
|
||||
#include <switch.h>
|
||||
|
||||
#define CONFIG_VAL_SECTION "values"
|
||||
|
||||
namespace config {
|
||||
|
||||
void Initialize();
|
||||
void Exit();
|
||||
|
||||
bool Refresh();
|
||||
bool HasProfilesLoaded();
|
||||
|
||||
std::uint8_t GetProfileCount(std::uint64_t tid);
|
||||
void GetProfiles(std::uint64_t tid, HocClkTitleProfileList* out_profiles);
|
||||
bool SetProfiles(std::uint64_t tid, HocClkTitleProfileList* profiles, bool immediate);
|
||||
std::uint32_t GetAutoClockHz(std::uint64_t tid, HocClkModule module, HocClkProfile profile, bool returnRaw);
|
||||
|
||||
void SetEnabled(bool enabled);
|
||||
bool Enabled();
|
||||
void SetOverrideHz(HocClkModule module, std::uint32_t hz);
|
||||
std::uint32_t GetOverrideHz(HocClkModule module);
|
||||
|
||||
std::uint64_t GetConfigValue(HocClkConfigValue val);
|
||||
const char* GetConfigValueName(HocClkConfigValue val, bool pretty);
|
||||
void GetConfigValues(HocClkConfigValueList* out_configValues);
|
||||
bool SetConfigValues(HocClkConfigValueList* configValues, bool immediate);
|
||||
bool ResetConfigValue(HocClkConfigValue kval);
|
||||
bool SetConfigValue(HocClkConfigValue kval, std::uint64_t value, bool immediate = true);
|
||||
|
||||
extern uint64_t configValues[HocClkConfigValue_EnumMax];
|
||||
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "file_utils.hpp"
|
||||
#include <nxExt.h>
|
||||
|
||||
extern "C" void __libnx_init_time(void);
|
||||
|
||||
namespace fileUtils {
|
||||
|
||||
namespace {
|
||||
|
||||
LockableMutex g_log_mutex;
|
||||
LockableMutex g_csv_mutex;
|
||||
std::atomic_bool g_has_initialized = false;
|
||||
bool g_log_enabled = false;
|
||||
std::uint64_t g_last_flag_check = 0;
|
||||
|
||||
void RefreshFlags(bool force) {
|
||||
std::uint64_t now = armTicksToNs(armGetSystemTick());
|
||||
if (!force && (now - g_last_flag_check) < FILE_FLAG_CHECK_INTERVAL_NS) {
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* file = fopen(FILE_LOG_FLAG_PATH, "r");
|
||||
if (file) {
|
||||
g_log_enabled = true;
|
||||
fclose(file);
|
||||
} else {
|
||||
g_log_enabled = false;
|
||||
}
|
||||
|
||||
g_last_flag_check = now;
|
||||
}
|
||||
|
||||
void InitializeThreadFunc(void* args) {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsInitialized() {
|
||||
return g_has_initialized;
|
||||
}
|
||||
|
||||
bool IsLogEnabled() {
|
||||
return g_log_enabled;
|
||||
}
|
||||
|
||||
void LogLine(const char* format, ...) {
|
||||
std::scoped_lock lock{g_log_mutex};
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
if (g_has_initialized) {
|
||||
RefreshFlags(false);
|
||||
|
||||
if (g_log_enabled) {
|
||||
FILE* file = fopen(FILE_LOG_FILE_PATH, "a");
|
||||
|
||||
if (file) {
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
fprintf(file, "[%lu] ", armGetSystemTick());
|
||||
vfprintf(file, format, args);
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void WriteContextToCsv(const HocClkContext* context) {
|
||||
std::scoped_lock lock{g_csv_mutex};
|
||||
|
||||
FILE* file = fopen(FILE_CONTEXT_CSV_PATH, "a");
|
||||
|
||||
if (file) {
|
||||
// Print header
|
||||
if (!ftell(file)) {
|
||||
fprintf(file, "timestamp,profile,app_tid");
|
||||
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
fprintf(file, ",%s_hz", hocclkFormatModule((HocClkModule)module, false));
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < HocClkThermalSensor_EnumMax; sensor++) {
|
||||
fprintf(file, ",%s_milliC", hocclkFormatThermalSensor((HocClkThermalSensor)sensor, false));
|
||||
}
|
||||
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
fprintf(file, ",%s_real_hz", hocclkFormatModule((HocClkModule)module, false));
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < HocClkPowerSensor_EnumMax; sensor++) {
|
||||
fprintf(file, ",%s_mw", hocclkFormatPowerSensor((HocClkPowerSensor)sensor, false));
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
|
||||
fprintf(file, "%ld%03ld,%s,%016lx", now.tv_sec, now.tv_nsec / 1000000UL, hocclkFormatProfile(context->profile, false), context->applicationId);
|
||||
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
fprintf(file, ",%d", context->freqs[module]);
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < HocClkThermalSensor_EnumMax; sensor++) {
|
||||
fprintf(file, ",%d", context->temps[sensor]);
|
||||
}
|
||||
|
||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||
fprintf(file, ",%d", context->realFreqs[module]);
|
||||
}
|
||||
|
||||
for (unsigned int sensor = 0; sensor < HocClkPowerSensor_EnumMax; sensor++) {
|
||||
fprintf(file, ",%d", context->power[sensor]);
|
||||
}
|
||||
|
||||
fprintf(file, "\n");
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeAsync() {
|
||||
Thread initThread = {0};
|
||||
threadCreate(&initThread, InitializeThreadFunc, NULL, NULL, 0x4000, 0x15, 0);
|
||||
threadStart(&initThread);
|
||||
}
|
||||
|
||||
Result Initialize() {
|
||||
Result rc = 0;
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = timeInitialize();
|
||||
}
|
||||
|
||||
__libnx_init_time();
|
||||
timeExit();
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = fsInitialize();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
rc = fsdevMountSdmc();
|
||||
}
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
RefreshFlags(true);
|
||||
g_has_initialized = true;
|
||||
LogLine("=== hoc-clk " TARGET_VERSION " ===");
|
||||
LogLine("by m4xw, natinusala, p-sam, Souldbminer, Lightos_ and Dominatorul");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void Exit() {
|
||||
if (!g_has_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_has_initialized = false;
|
||||
g_log_enabled = false;
|
||||
|
||||
fsdevUnmountAll();
|
||||
fsExit();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,381 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "governor.hpp"
|
||||
#include "process_management.hpp"
|
||||
namespace governor {
|
||||
|
||||
#define POLL_NS 5'000'000 // 5 ms – governor poll rate
|
||||
#define DOWN_HOLD_TICKS 10 // 50 ms – how long to in POLL_NS to hold while ramping down
|
||||
#define STEP_UTIL 900 // multiplier for step calculations
|
||||
|
||||
bool isGpuGovernorEnabled = false;
|
||||
bool isCpuGovernorEnabled = false;
|
||||
bool lastGpuGovernorState = false;
|
||||
bool lastCpuGovernorState = false;
|
||||
bool lastVrrGovernorState = false;
|
||||
bool hasChanged = true;
|
||||
bool isCpuGovernorInBoostMode = false;
|
||||
bool isVRREnabled = false;
|
||||
|
||||
// thread handles
|
||||
Thread cpuGovernorTHREAD;
|
||||
Thread gpuGovernorTHREAD;
|
||||
Thread vrrTHREAD;
|
||||
|
||||
void HandleGovernor(uint32_t targetHz)
|
||||
{
|
||||
u32 tempTargetHz = clockManager::gContext.overrideFreqs[HocClkModule_Governor];
|
||||
if (!tempTargetHz) {
|
||||
tempTargetHz = config::GetAutoClockHz(clockManager::gContext.applicationId, HocClkModule_Governor, clockManager::gContext.profile, true);
|
||||
if (!tempTargetHz)
|
||||
tempTargetHz = config::GetAutoClockHz(GLOBAL_PROFILE_ID, HocClkModule_Governor, clockManager::gContext.profile, true);
|
||||
}
|
||||
|
||||
auto resolve = [](u8 app, u8 temp) -> u8 {
|
||||
if (temp == ComponentGovernor_Disabled) return ComponentGovernor_Disabled;
|
||||
if (temp != ComponentGovernor_DoNotOverride) return temp;
|
||||
return app;
|
||||
};
|
||||
|
||||
u8 effectiveCpu = resolve(GovernorStateCpu(targetHz), GovernorStateCpu(tempTargetHz));
|
||||
u8 effectiveGpu = resolve(GovernorStateGpu(targetHz), GovernorStateGpu(tempTargetHz));
|
||||
u8 effectiveVrr = resolve(GovernorStateVrr(targetHz), GovernorStateVrr(tempTargetHz));
|
||||
|
||||
bool newCpuGovernorState = (effectiveCpu == ComponentGovernor_Enabled);
|
||||
bool newGpuGovernorState = (effectiveGpu == ComponentGovernor_Enabled);
|
||||
bool newVrrGovernorState = (effectiveVrr == ComponentGovernor_Enabled);
|
||||
|
||||
isCpuGovernorEnabled = newCpuGovernorState;
|
||||
isGpuGovernorEnabled = newGpuGovernorState;
|
||||
isVRREnabled = newVrrGovernorState;
|
||||
|
||||
if (newCpuGovernorState == false && lastCpuGovernorState == true) {
|
||||
svcSleepThread(100'000'000); // thread syncing. probably a cleaner way to do this but hey, it works!
|
||||
board::ResetToStockCpu();
|
||||
}
|
||||
if (newGpuGovernorState == false && lastGpuGovernorState == true) {
|
||||
svcSleepThread(100'000'000);
|
||||
board::ResetToStockGpu();
|
||||
}
|
||||
if (newVrrGovernorState == false && lastVrrGovernorState == true) {
|
||||
svcSleepThread(100'000'000);
|
||||
board::ResetToStockDisplay();
|
||||
}
|
||||
if (newCpuGovernorState != lastCpuGovernorState || newGpuGovernorState != lastGpuGovernorState || newVrrGovernorState != lastVrrGovernorState) {
|
||||
fileUtils::LogLine("[mgr] Governor state changed: CPU %s, GPU %s, VRR %s", newCpuGovernorState ? "enabled" : "disabled", newGpuGovernorState ? "enabled" : "disabled", newVrrGovernorState ? "enabled" : "disabled");
|
||||
lastCpuGovernorState = newCpuGovernorState;
|
||||
lastGpuGovernorState = newGpuGovernorState;
|
||||
lastVrrGovernorState = newVrrGovernorState;
|
||||
}
|
||||
}
|
||||
|
||||
u32 SchedutilTargetHz(u32 util, u32 tableMaxHz)
|
||||
{
|
||||
u64 hz = (u64)tableMaxHz * util / STEP_UTIL;
|
||||
return (u32)(std::min(hz, static_cast<u64>(tableMaxHz)));
|
||||
}
|
||||
|
||||
u32 TableIndexForHz(const clockManager::FreqTable& table, u32 targetHz)
|
||||
{
|
||||
for (u32 i = 0; i < table.count; i++)
|
||||
if (table.list[i] >= targetHz)
|
||||
return i;
|
||||
return table.count - 1;
|
||||
}
|
||||
|
||||
u32 ResolveTargetHz(HocClkModule module)
|
||||
{
|
||||
u32 hz = clockManager::gContext.overrideFreqs[module];
|
||||
if (!hz)
|
||||
hz = config::GetAutoClockHz(
|
||||
clockManager::gContext.applicationId, module,
|
||||
clockManager::gContext.profile, false);
|
||||
if (!hz)
|
||||
hz = config::GetAutoClockHz(
|
||||
GLOBAL_PROFILE_ID, module,
|
||||
clockManager::gContext.profile, false);
|
||||
return hz;
|
||||
}
|
||||
|
||||
void CpuGovernorThread(void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
u32 downHoldRemaining = 0;
|
||||
u32 lastHz = 0;
|
||||
u32 minHz = 612;
|
||||
u32 tick = 0;
|
||||
for (;;) {
|
||||
if (!clockManager::gRunning || !isCpuGovernorEnabled) {
|
||||
downHoldRemaining = 0;
|
||||
lastHz = 0;
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
|
||||
u32 mode = 0;
|
||||
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
|
||||
|
||||
if (R_SUCCEEDED(rc) && apmExtIsBoostMode(mode)) {
|
||||
isCpuGovernorInBoostMode = true;
|
||||
downHoldRemaining = 0;
|
||||
lastHz = 0;
|
||||
continue; // TODO: figure out a way to get boost clock easily and set it instead of just skipping the governor
|
||||
} else if (!apmExtIsBoostMode(mode)) {
|
||||
isCpuGovernorInBoostMode = false;
|
||||
}
|
||||
|
||||
auto& table = clockManager::gFreqTable[HocClkModule_CPU];
|
||||
|
||||
if (table.count == 0)
|
||||
continue;
|
||||
|
||||
std::scoped_lock lock{clockManager::gContextMutex};
|
||||
|
||||
u32 cpuLoad = board::GetPartLoad(HocClkPartLoad_CPUMax);
|
||||
|
||||
u32 tableMaxHz = table.list[table.count - 1];
|
||||
u32 desiredHz = SchedutilTargetHz(cpuLoad, tableMaxHz);
|
||||
u32 targetHz = ResolveTargetHz(HocClkModule_CPU);
|
||||
u32 maxHz = clockManager::GetMaxAllowedHz(HocClkModule_CPU, clockManager::gContext.profile);
|
||||
|
||||
if (targetHz && desiredHz > targetHz)
|
||||
desiredHz = targetHz;
|
||||
|
||||
if (maxHz && desiredHz > maxHz)
|
||||
desiredHz = maxHz;
|
||||
|
||||
u32 newHz = table.list[TableIndexForHz(table, desiredHz)];
|
||||
|
||||
// ramp up fast, go down slow
|
||||
bool goingDown = (lastHz != 0) && (newHz < lastHz);
|
||||
|
||||
if (!goingDown)
|
||||
downHoldRemaining = 0;
|
||||
else if (downHoldRemaining == 0)
|
||||
downHoldRemaining = DOWN_HOLD_TICKS;
|
||||
|
||||
if (downHoldRemaining > 0)
|
||||
downHoldRemaining--;
|
||||
|
||||
if (++tick > 50) {
|
||||
minHz = config::GetConfigValue(HocClkConfigValue_CpuGovernorMinimumFreq);
|
||||
tick = 0;
|
||||
}
|
||||
|
||||
if (newHz < minHz)
|
||||
newHz = minHz;
|
||||
|
||||
if ((!goingDown || (downHoldRemaining == 0)) && clockManager::IsAssignableHz(HocClkModule_CPU, newHz)) {
|
||||
board::SetHz(HocClkModule_CPU, newHz);
|
||||
clockManager::gContext.freqs[HocClkModule_CPU] = newHz;
|
||||
lastHz = newHz;
|
||||
}
|
||||
|
||||
svcSleepThread(POLL_NS);
|
||||
}
|
||||
}
|
||||
|
||||
void GovernorThread(void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
u32 downHoldRemaining = 0;
|
||||
u32 lastHz = 0;
|
||||
|
||||
for (;;) {
|
||||
if (!clockManager::gRunning || !isGpuGovernorEnabled) {
|
||||
downHoldRemaining = 0;
|
||||
lastHz = 0;
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& table = clockManager::gFreqTable[HocClkModule_GPU];
|
||||
if (table.count == 0)
|
||||
continue;
|
||||
|
||||
std::scoped_lock lock{clockManager::gContextMutex};
|
||||
|
||||
u32 gpuLoad = board::GetPartLoad(HocClkPartLoad_GPU);
|
||||
u32 tableMaxHz = table.list[table.count - 1];
|
||||
u32 desiredHz = SchedutilTargetHz(gpuLoad, tableMaxHz);
|
||||
u32 targetHz = ResolveTargetHz(HocClkModule_GPU);
|
||||
u32 maxHz = clockManager::GetMaxAllowedHz(HocClkModule_GPU, clockManager::gContext.profile);
|
||||
|
||||
if (targetHz && desiredHz > targetHz)
|
||||
desiredHz = targetHz;
|
||||
|
||||
if (maxHz && desiredHz > maxHz)
|
||||
desiredHz = maxHz;
|
||||
|
||||
u32 newHz = table.list[TableIndexForHz(table, desiredHz)];
|
||||
bool goingDown = (lastHz != 0) && (newHz < lastHz);
|
||||
|
||||
if (!goingDown)
|
||||
downHoldRemaining = 0;
|
||||
else if (downHoldRemaining == 0)
|
||||
downHoldRemaining = DOWN_HOLD_TICKS;
|
||||
|
||||
if (downHoldRemaining > 0)
|
||||
downHoldRemaining--;
|
||||
|
||||
if ((!goingDown || (downHoldRemaining == 0)) && clockManager::IsAssignableHz(HocClkModule_GPU, newHz)) {
|
||||
board::SetHz(HocClkModule_GPU, newHz);
|
||||
clockManager::gContext.freqs[HocClkModule_GPU] = newHz;
|
||||
lastHz = newHz;
|
||||
}
|
||||
|
||||
svcSleepThread(POLL_NS);
|
||||
}
|
||||
}
|
||||
|
||||
void VRRThread(void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
u8 tick = 0, tick2 = 0;
|
||||
for (;;) {
|
||||
if (!clockManager::gRunning || clockManager::gContext.profile == HocClkProfile_Docked || !isVRREnabled) {
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{clockManager::gContextMutex};
|
||||
|
||||
if(++tick2 > 100) {
|
||||
bool isApplicationOutOfFocus = false;
|
||||
Result rc = processManagement::isApplicationOutOfFocus(&isApplicationOutOfFocus);
|
||||
if(R_FAILED(rc)) {
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isApplicationOutOfFocus) {
|
||||
board::ResetToStockDisplay();
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
u8 fps;
|
||||
|
||||
if (clockManager::gContext.isSaltyNXInstalled) {
|
||||
fps = integrations::GetSaltyNXFPS();
|
||||
} else {
|
||||
svcSleepThread(~0ULL); // effectively disable the thread if SaltyNX isn't installed, as there's no point in it running
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fps == 254) {
|
||||
svcSleepThread(POLL_NS);
|
||||
continue;
|
||||
}
|
||||
// if(appletGetFocusState() != AppletFocusState_InFocus) {
|
||||
// board::ResetToStockDisplay();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
u32 targetHz = clockManager::gContext.overrideFreqs[HocClkModule_Display];
|
||||
if (!targetHz) {
|
||||
targetHz = config::GetAutoClockHz(clockManager::gContext.applicationId, HocClkModule_Display, clockManager::gContext.profile, false);
|
||||
if (!targetHz)
|
||||
targetHz = config::GetAutoClockHz(GLOBAL_PROFILE_ID, HocClkModule_Display, clockManager::gContext.profile, false);
|
||||
}
|
||||
|
||||
u8 maxDisplay;
|
||||
if (targetHz) {
|
||||
maxDisplay = targetHz;
|
||||
} else {
|
||||
maxDisplay = 60; // don't assume display stuff!
|
||||
}
|
||||
|
||||
u8 minDisplay = board::GetConsoleType() == HocClkConsoleType_Aula ? 45 : 40;
|
||||
if (maxDisplay == minDisplay)
|
||||
continue;
|
||||
|
||||
if (fps >= minDisplay && fps <= maxDisplay) {
|
||||
board::SetHz(HocClkModule_Display, fps);
|
||||
clockManager::gContext.freqs[HocClkModule_Display] = fps;
|
||||
clockManager::gContext.realFreqs[HocClkModule_Display] = fps;
|
||||
} else {
|
||||
for (u32 i = 0; i < 10; i++) {
|
||||
u32 compareHz = fps * i;
|
||||
if (compareHz >= minDisplay && compareHz <= maxDisplay) {
|
||||
board::SetHz(HocClkModule_Display, compareHz);
|
||||
clockManager::gContext.freqs[HocClkModule_Display] = compareHz;
|
||||
clockManager::gContext.realFreqs[HocClkModule_Display] = compareHz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (++tick > 50) {
|
||||
board::SetHz(HocClkModule_Display, maxDisplay);
|
||||
tick = 0;
|
||||
svcSleepThread(50'000'000);
|
||||
}
|
||||
|
||||
svcSleepThread(POLL_NS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void startThreads() {
|
||||
|
||||
threadCreate(
|
||||
&cpuGovernorTHREAD,
|
||||
CpuGovernorThread,
|
||||
nullptr,
|
||||
NULL,
|
||||
0x2000,
|
||||
0x3F,
|
||||
-2
|
||||
);
|
||||
|
||||
threadCreate(
|
||||
&gpuGovernorTHREAD,
|
||||
GovernorThread,
|
||||
nullptr,
|
||||
NULL,
|
||||
0x2000,
|
||||
0x3F,
|
||||
-2
|
||||
);
|
||||
|
||||
threadCreate(
|
||||
&vrrTHREAD,
|
||||
VRRThread,
|
||||
nullptr,
|
||||
NULL,
|
||||
0x2000,
|
||||
0x3F,
|
||||
-2
|
||||
);
|
||||
|
||||
threadStart(&cpuGovernorTHREAD);
|
||||
threadStart(&gpuGovernorTHREAD);
|
||||
threadStart(&vrrTHREAD);
|
||||
}
|
||||
|
||||
void exitThreads() {
|
||||
threadClose(&cpuGovernorTHREAD);
|
||||
threadClose(&gpuGovernorTHREAD);
|
||||
threadClose(&vrrTHREAD);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include "board/board.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include <cstring>
|
||||
#include "file_utils.hpp"
|
||||
#include "board/board.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "config.hpp"
|
||||
#include "integrations.hpp"
|
||||
#include <nxExt/cpp/lockable_mutex.h>
|
||||
|
||||
namespace governor {
|
||||
extern bool isCpuGovernorInBoostMode;
|
||||
extern bool isVRREnabled;
|
||||
extern bool isGpuGovernorEnabled;
|
||||
extern bool isCpuGovernorEnabled;
|
||||
extern bool lastGpuGovernorState;
|
||||
extern bool lastCpuGovernorState;
|
||||
extern bool lastVrrGovernorState;
|
||||
void startThreads();
|
||||
void exitThreads();
|
||||
void HandleGovernor(uint32_t targetHz);
|
||||
void GovernorThread(void* arg);
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "integrations.hpp"
|
||||
#include <sys/stat.h>
|
||||
#include <SaltyNX.h>
|
||||
#include "process_management.hpp"
|
||||
|
||||
namespace integrations {
|
||||
|
||||
namespace {
|
||||
|
||||
NxFpsSharedBlock* gNxFps = nullptr;
|
||||
SharedMemory gSharedMemory = {};
|
||||
bool gSharedMemoryUsed = false;
|
||||
Handle gRemoteSharedMemory = 1;
|
||||
u64 gPrevTid = 0;
|
||||
|
||||
bool CheckSaltyNXPort() {
|
||||
Handle saltysd;
|
||||
|
||||
for (int i = 0; i < 67; i++) {
|
||||
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||
svcCloseHandle(saltysd);
|
||||
break;
|
||||
}
|
||||
if (i == 66) return false;
|
||||
svcSleepThread(1'000'000);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 67; i++) {
|
||||
if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) {
|
||||
svcCloseHandle(saltysd);
|
||||
return true;
|
||||
}
|
||||
svcSleepThread(1'000'000);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SearchSharedMemoryBlock(uintptr_t base) {
|
||||
ptrdiff_t search_offset = 0;
|
||||
while (search_offset < 0x1000) {
|
||||
gNxFps = (NxFpsSharedBlock*)(base + search_offset);
|
||||
if (gNxFps->MAGIC == 0x465053)
|
||||
return;
|
||||
search_offset += 4;
|
||||
}
|
||||
gNxFps = nullptr;
|
||||
}
|
||||
|
||||
void LoadSharedMemory() {
|
||||
if (SaltySD_Connect())
|
||||
return;
|
||||
SaltySD_GetSharedMemoryHandle(&gRemoteSharedMemory);
|
||||
SaltySD_Term();
|
||||
shmemLoadRemote(&gSharedMemory, gRemoteSharedMemory, 0x1000, Perm_Rw);
|
||||
if (!shmemMap(&gSharedMemory))
|
||||
gSharedMemoryUsed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool GetSysDockState() {
|
||||
struct stat st = {0};
|
||||
return stat("sdmc:/atmosphere/contents/42000000000000A0/flags/boot2.flag", &st) == 0;
|
||||
}
|
||||
|
||||
bool GetSaltyNXState() {
|
||||
struct stat st = {0};
|
||||
return stat("sdmc:/atmosphere/contents/0000000000534C56/flags/boot2.flag", &st) == 0;
|
||||
}
|
||||
|
||||
bool GetRETROSuperStatus() {
|
||||
struct stat st = {0};
|
||||
return stat("sdmc:/config/horizon-oc/retro.flag", &st) == 0; // TODO: unhardcode this
|
||||
}
|
||||
|
||||
void LoadSaltyNX() {
|
||||
if (!CheckSaltyNXPort())
|
||||
return;
|
||||
LoadSharedMemory();
|
||||
}
|
||||
|
||||
u8 GetSaltyNXFPS() {
|
||||
if (!gSharedMemoryUsed)
|
||||
return 254;
|
||||
|
||||
u64 tid = processManagement::GetCurrentApplicationId();
|
||||
if (tid == 0)
|
||||
return 254;
|
||||
|
||||
if (gPrevTid != tid) {
|
||||
gNxFps = nullptr;
|
||||
gPrevTid = tid;
|
||||
}
|
||||
|
||||
if (!gNxFps) {
|
||||
uintptr_t base = (uintptr_t)shmemGetAddr(&gSharedMemory);
|
||||
SearchSharedMemoryBlock(base);
|
||||
}
|
||||
|
||||
return gNxFps ? gNxFps->FPS : 254;
|
||||
}
|
||||
|
||||
u16 GetSaltyNXResolutionHeight() {
|
||||
if (!gSharedMemoryUsed)
|
||||
return 0;
|
||||
|
||||
u64 tid = processManagement::GetCurrentApplicationId();
|
||||
if (tid == 0)
|
||||
return 0;
|
||||
|
||||
if (gPrevTid != tid) {
|
||||
gNxFps = nullptr;
|
||||
gPrevTid = tid;
|
||||
}
|
||||
|
||||
if (!gNxFps) {
|
||||
uintptr_t base = (uintptr_t)shmemGetAddr(&gSharedMemory);
|
||||
SearchSharedMemoryBlock(base);
|
||||
}
|
||||
|
||||
if (gNxFps) {
|
||||
gNxFps->renderCalls[0].calls = 0xFFFF;
|
||||
svcSleepThread(10*1000);
|
||||
return gNxFps->renderCalls[0].height == 0 ? gNxFps->viewportCalls[0].height : gNxFps->renderCalls[0].height;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
|
||||
namespace integrations {
|
||||
|
||||
struct NxFpsSharedBlock {
|
||||
uint32_t MAGIC;
|
||||
uint8_t FPS;
|
||||
float FPSavg;
|
||||
bool pluginActive;
|
||||
uint8_t FPSlocked;
|
||||
uint8_t FPSmode;
|
||||
uint8_t ZeroSync;
|
||||
uint8_t patchApplied;
|
||||
uint8_t API;
|
||||
uint32_t FPSticks[10];
|
||||
uint8_t Buffers;
|
||||
uint8_t SetBuffers;
|
||||
uint8_t ActiveBuffers;
|
||||
uint8_t SetActiveBuffers;
|
||||
union {
|
||||
struct {
|
||||
bool handheld: 1;
|
||||
bool docked: 1;
|
||||
unsigned int reserved: 6;
|
||||
} NX_PACKED ds;
|
||||
uint8_t general;
|
||||
} displaySync;
|
||||
struct resolutionCalls {
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t calls;
|
||||
} renderCalls[8], viewportCalls[8];
|
||||
bool forceOriginalRefreshRate;
|
||||
bool dontForce60InDocked;
|
||||
bool forceSuspend;
|
||||
uint8_t currentRefreshRate;
|
||||
float readSpeedPerSecond;
|
||||
uint8_t FPSlockedDocked;
|
||||
uint64_t frameNumber;
|
||||
} NX_PACKED;
|
||||
|
||||
bool GetSysDockState();
|
||||
bool GetSaltyNXState();
|
||||
bool GetRETROSuperStatus();
|
||||
void LoadSaltyNX();
|
||||
u8 GetSaltyNXFPS();
|
||||
u16 GetSaltyNXResolutionHeight();
|
||||
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "ipc_service.hpp"
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
#include <nxExt.h>
|
||||
#include "file_utils.hpp"
|
||||
#include "errors.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include "config.hpp"
|
||||
#include "kip.hpp"
|
||||
namespace ipcService {
|
||||
|
||||
namespace {
|
||||
|
||||
bool gRunning = false;
|
||||
Thread gThread;
|
||||
LockableMutex gThreadMutex;
|
||||
IpcServer gServer;
|
||||
|
||||
Result GetApiVersion(u32* out_version) {
|
||||
*out_version = HOCCLK_IPC_API_VERSION;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetVersionString(char* out_buf, size_t bufSize) {
|
||||
if (bufSize) {
|
||||
strncpy(out_buf, TARGET_VERSION, bufSize-1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetCurrentContext(HocClkContext* out_ctx) {
|
||||
*out_ctx = clockManager::GetCurrentContext();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result ExitHandler() {
|
||||
clockManager::SetRunning(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return HOCCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
*out_count = config::GetProfileCount(*tid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetProfiles(std::uint64_t* tid, HocClkTitleProfileList* out_profiles) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return HOCCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
config::GetProfiles(*tid, out_profiles);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result SetProfiles(HocClkIpc_SetProfiles_Args* args) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return HOCCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
HocClkTitleProfileList profiles = args->profiles;
|
||||
if (!config::SetProfiles(args->tid, &profiles, true)) {
|
||||
return HOCCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result SetEnabled(std::uint8_t* enabled) {
|
||||
config::SetEnabled(*enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result SetOverride(HocClkIpc_SetOverride_Args* args) {
|
||||
if (!HOCCLK_ENUM_VALID(HocClkModule, args->module)) {
|
||||
return HOCCLK_ERROR(Generic);
|
||||
}
|
||||
config::SetOverrideHz(args->module, args->hz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetConfigValuesHandler(HocClkConfigValueList* out_configValues) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return HOCCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
config::GetConfigValues(out_configValues);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result SetConfigValuesHandler(HocClkConfigValueList* configValues) {
|
||||
if (!config::HasProfilesLoaded()) {
|
||||
return HOCCLK_ERROR(ConfigNotLoaded);
|
||||
}
|
||||
HocClkConfigValueList copy = *configValues;
|
||||
if (!config::SetConfigValues(©, true)) {
|
||||
return HOCCLK_ERROR(ConfigSaveFailed);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result GetFreqList(HocClkIpc_GetFreqList_Args* args, std::uint32_t* out_list, std::size_t size, std::uint32_t* out_count) {
|
||||
if (!HOCCLK_ENUM_VALID(HocClkModule, args->module)) {
|
||||
return HOCCLK_ERROR(Generic);
|
||||
}
|
||||
if (args->maxCount != size/sizeof(*out_list)) {
|
||||
return HOCCLK_ERROR(Generic);
|
||||
}
|
||||
clockManager::GetFreqList(args->module, out_list, args->maxCount, out_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8* out_data, size_t* out_dataSize) {
|
||||
(void)arg;
|
||||
switch (r->data.cmdId) {
|
||||
case HocClkIpcCmd_GetApiVersion:
|
||||
*out_dataSize = sizeof(u32);
|
||||
return GetApiVersion((u32*)out_data);
|
||||
|
||||
case HocClkIpcCmd_GetVersionString:
|
||||
if (r->hipc.meta.num_recv_buffers >= 1) {
|
||||
return GetVersionString(
|
||||
(char*)hipcGetBufferAddress(r->hipc.data.recv_buffers),
|
||||
hipcGetBufferSize(r->hipc.data.recv_buffers)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_GetCurrentContext:
|
||||
if (r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1) {
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||
if (bufSize >= sizeof(HocClkContext)) {
|
||||
return GetCurrentContext((HocClkContext*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_Exit:
|
||||
return ExitHandler();
|
||||
|
||||
case HocClkIpcCmd_GetProfileCount:
|
||||
if (r->data.size >= sizeof(std::uint64_t)) {
|
||||
*out_dataSize = sizeof(std::uint8_t);
|
||||
return GetProfileCount((std::uint64_t*)r->data.ptr, (std::uint8_t*)out_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_GetProfiles:
|
||||
if (r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1) {
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||
if (bufSize >= sizeof(HocClkTitleProfileList)) {
|
||||
return GetProfiles((std::uint64_t*)r->data.ptr, (HocClkTitleProfileList*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_SetProfiles:
|
||||
if (r->data.size >= sizeof(HocClkIpc_SetProfiles_Args)) {
|
||||
return SetProfiles((HocClkIpc_SetProfiles_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_SetEnabled:
|
||||
if (r->data.size >= sizeof(std::uint8_t)) {
|
||||
return SetEnabled((std::uint8_t*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_SetOverride:
|
||||
if (r->data.size >= sizeof(HocClkIpc_SetOverride_Args)) {
|
||||
return SetOverride((HocClkIpc_SetOverride_Args*)r->data.ptr);
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_GetConfigValues:
|
||||
if (r->hipc.meta.num_recv_buffers >= 1) {
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
|
||||
if (bufSize >= sizeof(HocClkConfigValueList)) {
|
||||
return GetConfigValuesHandler((HocClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_SetConfigValues:
|
||||
if (r->hipc.meta.num_send_buffers >= 1) {
|
||||
size_t bufSize = hipcGetBufferSize(r->hipc.data.send_buffers);
|
||||
if (bufSize >= sizeof(HocClkConfigValueList)) {
|
||||
return SetConfigValuesHandler((HocClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.send_buffers));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_GetFreqList:
|
||||
if (r->data.size >= sizeof(HocClkIpc_GetFreqList_Args) && r->hipc.meta.num_recv_buffers >= 1) {
|
||||
*out_dataSize = sizeof(std::uint32_t);
|
||||
return GetFreqList(
|
||||
(HocClkIpc_GetFreqList_Args*)r->data.ptr,
|
||||
(std::uint32_t*)hipcGetBufferAddress(r->hipc.data.recv_buffers),
|
||||
hipcGetBufferSize(r->hipc.data.recv_buffers),
|
||||
(std::uint32_t*)out_data
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case HocClkIpcCmd_SetKipData:
|
||||
if (r->data.size >= 0) {
|
||||
kip::SetKipData();
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return HOCCLK_ERROR(Generic);
|
||||
}
|
||||
|
||||
void ProcessThreadFunc(void* arg) {
|
||||
(void)arg;
|
||||
Result rc;
|
||||
while (true) {
|
||||
rc = ipcServerProcess(&gServer, &ServiceHandlerFunc, nullptr);
|
||||
if (R_FAILED(rc)) {
|
||||
if (rc == KERNELRESULT(Cancelled)) {
|
||||
return;
|
||||
}
|
||||
if (rc != KERNELRESULT(ConnectionClosed)) {
|
||||
fileUtils::LogLine("[ipc] ipcServerProcess: [0x%x] %04d-%04d", rc, R_MODULE(rc), R_DESCRIPTION(rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
std::int32_t priority;
|
||||
Result rc = svcGetThreadPriority(&priority, CUR_THREAD_HANDLE);
|
||||
ASSERT_RESULT_OK(rc, "svcGetThreadPriority");
|
||||
rc = ipcServerInit(&gServer, HOCCLK_IPC_SERVICE_NAME, 42);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerInit");
|
||||
rc = threadCreate(&gThread, &ProcessThreadFunc, nullptr, NULL, 0x2000, priority, -2);
|
||||
ASSERT_RESULT_OK(rc, "threadCreate");
|
||||
gRunning = false;
|
||||
}
|
||||
|
||||
void Exit() {
|
||||
SetRunning(false);
|
||||
Result rc = threadClose(&gThread);
|
||||
ASSERT_RESULT_OK(rc, "threadClose");
|
||||
rc = ipcServerExit(&gServer);
|
||||
ASSERT_RESULT_OK(rc, "ipcServerExit");
|
||||
}
|
||||
|
||||
void SetRunning(bool running) {
|
||||
std::scoped_lock lock{gThreadMutex};
|
||||
if (gRunning == running) {
|
||||
return;
|
||||
}
|
||||
|
||||
gRunning = running;
|
||||
|
||||
if (running) {
|
||||
Result rc = threadStart(&gThread);
|
||||
ASSERT_RESULT_OK(rc, "threadStart");
|
||||
} else {
|
||||
svcCancelSynchronization(gThread.handle);
|
||||
threadWaitForExit(&gThread);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,246 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kip.hpp"
|
||||
#include "board/board.hpp"
|
||||
|
||||
namespace kip {
|
||||
|
||||
bool kipAvailable = false;
|
||||
|
||||
void SetKipData()
|
||||
{
|
||||
// TODO: figure out if this REALLY causes issues (i doubt it)
|
||||
// if(board::GetSocType() == HocClkSocType_Mariko) {
|
||||
// if(R_FAILED(I2c_BuckConverter_SetMvOut(&I2c_Mariko_DRAM_VDDQ, config::GetConfigValue(KipConfigValue_marikoEmcVddqVolt) / 1000))) {
|
||||
// fileUtils::LogLine("[clock_manager] Failed set i2c vddq");
|
||||
// notification::writeNotification("Horizon OC\nFailed to write I2C\nwhile setting vddq");
|
||||
// }
|
||||
// }
|
||||
CustomizeTable table;
|
||||
FILE* fp;
|
||||
fp = fopen("sdmc:/atmosphere/kips/hoc.kip", "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
notification::writeNotification("Horizon OC\nKip opening failed");
|
||||
kipAvailable = false;
|
||||
return;
|
||||
} else {
|
||||
kipAvailable = true;
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!cust_read_and_cache("sdmc:/atmosphere/kips/hoc.kip", &table)) {
|
||||
fileUtils::LogLine("[clock_manager] Failed to read KIP file");
|
||||
notification::writeNotification("Horizon OC\nKip read failed");
|
||||
return;
|
||||
}
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, custRev, config::GetConfigValue(KipConfigValue_custRev));
|
||||
// CUST_WRITE_FIELD_BATCH(&table, mtcConf, config::GetConfigValue(KipConfigValue_mtcConf));
|
||||
CUST_WRITE_FIELD_BATCH(&table, hpMode, config::GetConfigValue(KipConfigValue_hpMode));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, commonEmcMemVolt, config::GetConfigValue(KipConfigValue_commonEmcMemVolt));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock, config::GetConfigValue(KipConfigValue_eristaEmcMaxClock));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock1, config::GetConfigValue(KipConfigValue_eristaEmcMaxClock1));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock2, config::GetConfigValue(KipConfigValue_eristaEmcMaxClock2));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoEmcMaxClock, config::GetConfigValue(KipConfigValue_marikoEmcMaxClock));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoEmcVddqVolt, config::GetConfigValue(KipConfigValue_marikoEmcVddqVolt));
|
||||
CUST_WRITE_FIELD_BATCH(&table, emcDvbShift, config::GetConfigValue(KipConfigValue_emcDvbShift));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, t1_tRCD, config::GetConfigValue(KipConfigValue_t1_tRCD));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t2_tRP, config::GetConfigValue(KipConfigValue_t2_tRP));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t3_tRAS, config::GetConfigValue(KipConfigValue_t3_tRAS));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t4_tRRD, config::GetConfigValue(KipConfigValue_t4_tRRD));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t5_tRFC, config::GetConfigValue(KipConfigValue_t5_tRFC));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t6_tRTW, config::GetConfigValue(KipConfigValue_t6_tRTW));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR, config::GetConfigValue(KipConfigValue_t7_tWTR));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t8_tREFI, config::GetConfigValue(KipConfigValue_t8_tREFI));
|
||||
CUST_WRITE_FIELD_BATCH(&table, mem_burst_read_latency, config::GetConfigValue(KipConfigValue_mem_burst_read_latency));
|
||||
CUST_WRITE_FIELD_BATCH(&table, mem_burst_write_latency, config::GetConfigValue(KipConfigValue_mem_burst_write_latency));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuUV, config::GetConfigValue(KipConfigValue_eristaCpuUV));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuVmin, config::GetConfigValue(KipConfigValue_eristaCpuVmin));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuMaxVolt, config::GetConfigValue(KipConfigValue_eristaCpuMaxVolt));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuUnlock, config::GetConfigValue(KipConfigValue_eristaCpuUnlock));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuUVLow, config::GetConfigValue(KipConfigValue_marikoCpuUVLow));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuUVHigh, config::GetConfigValue(KipConfigValue_marikoCpuUVHigh));
|
||||
CUST_WRITE_FIELD_BATCH(&table, tableConf, config::GetConfigValue(KipConfigValue_tableConf));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuLowVmin, config::GetConfigValue(KipConfigValue_marikoCpuLowVmin));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuHighVmin, config::GetConfigValue(KipConfigValue_marikoCpuHighVmin));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuMaxVolt, config::GetConfigValue(KipConfigValue_marikoCpuMaxVolt));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuMaxClock, config::GetConfigValue(KipConfigValue_marikoCpuMaxClock));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaCpuBoostClock, config::GetConfigValue(KipConfigValue_eristaCpuBoostClock));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoCpuBoostClock, config::GetConfigValue(KipConfigValue_marikoCpuBoostClock));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaGpuUV, config::GetConfigValue(KipConfigValue_eristaGpuUV));
|
||||
CUST_WRITE_FIELD_BATCH(&table, eristaGpuVmin, config::GetConfigValue(KipConfigValue_eristaGpuVmin));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoGpuUV, config::GetConfigValue(KipConfigValue_marikoGpuUV));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoGpuVmin, config::GetConfigValue(KipConfigValue_marikoGpuVmin));
|
||||
CUST_WRITE_FIELD_BATCH(&table, marikoGpuVmax, config::GetConfigValue(KipConfigValue_marikoGpuVmax));
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, commonGpuVoltOffset, config::GetConfigValue(KipConfigValue_commonGpuVoltOffset));
|
||||
CUST_WRITE_FIELD_BATCH(&table, gpuSpeedo, config::GetConfigValue(KipConfigValue_gpuSpeedo));
|
||||
|
||||
for (int i = 0; i < 24; i++) {
|
||||
table.marikoGpuVoltArray[i] = config::GetConfigValue((HocClkConfigValue)(KipConfigValue_g_volt_76800 + i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
table.eristaGpuVoltArray[i] = config::GetConfigValue((HocClkConfigValue)(KipConfigValue_g_volt_e_76800 + i));
|
||||
}
|
||||
|
||||
CUST_WRITE_FIELD_BATCH(&table, t6_tRTW_fine_tune, config::GetConfigValue(KipConfigValue_t6_tRTW_fine_tune));
|
||||
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR_fine_tune, config::GetConfigValue(KipConfigValue_t7_tWTR_fine_tune));
|
||||
|
||||
if (!cust_write_table("sdmc:/atmosphere/kips/hoc.kip", &table)) {
|
||||
fileUtils::LogLine("[clock_manager] Failed to write KIP file");
|
||||
notification::writeNotification("Horizon OC\nKip write failed");
|
||||
}
|
||||
|
||||
HocClkConfigValueList configValues;
|
||||
config::GetConfigValues(&configValues);
|
||||
|
||||
configValues.values[KipCrc32] = (u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum
|
||||
|
||||
if (config::SetConfigValues(&configValues, false)) {
|
||||
fileUtils::LogLine("[clock_manager] Successfully loaded KIP data into config");
|
||||
} else {
|
||||
fileUtils::LogLine("[clock_manager] Warning: Failed to set config values from KIP");
|
||||
notification::writeNotification("Horizon OC\nKip config set failed");
|
||||
}
|
||||
}
|
||||
|
||||
// I know this is very hacky, but the config system in the sysmodule doesn't really support writing
|
||||
|
||||
void GetKipData()
|
||||
{
|
||||
FILE* fp;
|
||||
if (config::Refresh()) {
|
||||
fp = fopen("sdmc:/atmosphere/kips/hoc.kip", "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
notification::writeNotification("Horizon OC\nKip opening failed");
|
||||
kipAvailable = false;
|
||||
return;
|
||||
} else {
|
||||
kipAvailable = true;
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
HocClkConfigValueList configValues;
|
||||
config::GetConfigValues(&configValues);
|
||||
|
||||
CustomizeTable table;
|
||||
|
||||
if (!cust_read_and_cache("sdmc:/atmosphere/kips/hoc.kip", &table)) {
|
||||
fileUtils::LogLine("[clock_manager] Failed to read KIP file for GetKipData");
|
||||
notification::writeNotification("Horizon OC\nKip read failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip") != config::GetConfigValue(KipCrc32) && !config::GetConfigValue(HocClkConfigValue_IsFirstLoad)) {
|
||||
SetKipData();
|
||||
notification::writeNotification("Horizon OC\nKIP has been updated");
|
||||
notification::writeNotification("Horizon OC\nPlease reboot your console");
|
||||
notification::writeNotification("Horizon OC\nto complete the update");
|
||||
return;
|
||||
}
|
||||
if (config::GetConfigValue(HocClkConfigValue_IsFirstLoad) == true) {
|
||||
configValues.values[HocClkConfigValue_IsFirstLoad] = (u64)false;
|
||||
notification::writeNotification("Horizon OC has been installed");
|
||||
}
|
||||
|
||||
configValues.values[KipCrc32] = (u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum
|
||||
// configValues.values[KipConfigValue_mtcConf] = cust_get_mtc_conf(&table);
|
||||
configValues.values[KipConfigValue_hpMode] = cust_get_hp_mode(&table);
|
||||
|
||||
configValues.values[KipConfigValue_commonEmcMemVolt] = cust_get_common_emc_volt(&table);
|
||||
configValues.values[KipConfigValue_eristaEmcMaxClock] = cust_get_erista_emc_max(&table);
|
||||
configValues.values[KipConfigValue_eristaEmcMaxClock1] = cust_get_erista_emc_max1(&table);
|
||||
configValues.values[KipConfigValue_eristaEmcMaxClock2] = cust_get_erista_emc_max2(&table);
|
||||
configValues.values[KipConfigValue_marikoEmcMaxClock] = cust_get_mariko_emc_max(&table);
|
||||
configValues.values[KipConfigValue_marikoEmcVddqVolt] = cust_get_mariko_emc_vddq(&table);
|
||||
configValues.values[KipConfigValue_emcDvbShift] = cust_get_emc_dvb_shift(&table);
|
||||
|
||||
configValues.values[KipConfigValue_t1_tRCD] = cust_get_tRCD(&table);
|
||||
configValues.values[KipConfigValue_t2_tRP] = cust_get_tRP(&table);
|
||||
configValues.values[KipConfigValue_t3_tRAS] = cust_get_tRAS(&table);
|
||||
configValues.values[KipConfigValue_t4_tRRD] = cust_get_tRRD(&table);
|
||||
configValues.values[KipConfigValue_t5_tRFC] = cust_get_tRFC(&table);
|
||||
configValues.values[KipConfigValue_t6_tRTW] = cust_get_tRTW(&table);
|
||||
configValues.values[KipConfigValue_t7_tWTR] = cust_get_tWTR(&table);
|
||||
configValues.values[KipConfigValue_t8_tREFI] = cust_get_tREFI(&table);
|
||||
configValues.values[KipConfigValue_mem_burst_read_latency] = cust_get_burst_read_lat(&table);
|
||||
configValues.values[KipConfigValue_mem_burst_write_latency] = cust_get_burst_write_lat(&table);
|
||||
|
||||
configValues.values[KipConfigValue_eristaCpuUV] = cust_get_erista_cpu_uv(&table);
|
||||
configValues.values[KipConfigValue_eristaCpuVmin] = cust_get_eristaCpuVmin(&table);
|
||||
configValues.values[KipConfigValue_eristaCpuMaxVolt] = cust_get_erista_cpu_max_volt(&table);
|
||||
configValues.values[KipConfigValue_eristaCpuUnlock] = cust_get_eristaCpuUnlock(&table);
|
||||
|
||||
configValues.values[KipConfigValue_marikoCpuUVLow] = cust_get_mariko_cpu_uv_low(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuUVHigh] = cust_get_mariko_cpu_uv_high(&table);
|
||||
configValues.values[KipConfigValue_tableConf] = cust_get_table_conf(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuLowVmin] = cust_get_mariko_cpu_low_vmin(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuHighVmin] = cust_get_mariko_cpu_high_vmin(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuMaxVolt] = cust_get_mariko_cpu_max_volt(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuMaxClock] = cust_get_marikoCpuMaxClock(&table);
|
||||
configValues.values[KipConfigValue_eristaCpuBoostClock] = cust_get_erista_cpu_boost(&table);
|
||||
configValues.values[KipConfigValue_marikoCpuBoostClock] = cust_get_mariko_cpu_boost(&table);
|
||||
|
||||
configValues.values[KipConfigValue_eristaGpuUV] = cust_get_erista_gpu_uv(&table);
|
||||
configValues.values[KipConfigValue_eristaGpuVmin] = cust_get_erista_gpu_vmin(&table);
|
||||
configValues.values[KipConfigValue_marikoGpuUV] = cust_get_mariko_gpu_uv(&table);
|
||||
configValues.values[KipConfigValue_marikoGpuVmin] = cust_get_mariko_gpu_vmin(&table);
|
||||
configValues.values[KipConfigValue_marikoGpuVmax] = cust_get_mariko_gpu_vmax(&table);
|
||||
configValues.values[KipConfigValue_commonGpuVoltOffset] = cust_get_common_gpu_offset(&table);
|
||||
configValues.values[KipConfigValue_gpuSpeedo] = board::GetFuseData()->gpuSpeedo; // cust_get_gpu_speedo(&table);
|
||||
|
||||
for (int i = 0; i < 24; i++) {
|
||||
configValues.values[KipConfigValue_g_volt_76800 + i] = cust_get_mariko_gpu_volt(&table, i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 27; i++) {
|
||||
configValues.values[KipConfigValue_g_volt_e_76800 + i] = cust_get_erista_gpu_volt(&table, i);
|
||||
}
|
||||
|
||||
configValues.values[KipConfigValue_t7_tWTR_fine_tune] = cust_get_tWTR_fine_tune(&table);
|
||||
configValues.values[KipConfigValue_t6_tRTW_fine_tune] = cust_get_tRTW_fine_tune(&table);
|
||||
|
||||
// if(cust_get_cust_rev(&table) == KIP_CUST_REV)
|
||||
// return;
|
||||
|
||||
if (sizeof(HocClkConfigValueList) <= sizeof(configValues)) {
|
||||
if (config::SetConfigValues(&configValues, false)) {
|
||||
fileUtils::LogLine("[clock_manager] Successfully loaded KIP data into config");
|
||||
} else {
|
||||
fileUtils::LogLine("[clock_manager] Warning: Failed to set config values from KIP");
|
||||
notification::writeNotification("Horizon OC\nKip config set failed");
|
||||
}
|
||||
} else {
|
||||
fileUtils::LogLine("[clock_manager] Error: Config value list buffer size mismatch");
|
||||
notification::writeNotification("Horizon OC\nConfig Buffer Mismatch");
|
||||
}
|
||||
} else {
|
||||
fileUtils::LogLine("[clock_manager] Config refresh error in GetKipData!");
|
||||
notification::writeNotification("Horizon OC\nConfig refresh failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,458 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "config.hpp"
|
||||
#include "file_utils.hpp"
|
||||
#include <notification.h>
|
||||
#include <crc32.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
namespace kip {
|
||||
extern bool kipAvailable;
|
||||
|
||||
typedef struct {
|
||||
u8 cust[4];
|
||||
u32 custRev;
|
||||
u32 placeholder;
|
||||
u32 hpMode;
|
||||
u32 commonEmcMemVolt;
|
||||
u32 eristaEmcMaxClock;
|
||||
u32 eristaEmcMaxClock1;
|
||||
u32 eristaEmcMaxClock2;
|
||||
u32 marikoEmcMaxClock;
|
||||
u32 marikoEmcVddqVolt;
|
||||
u32 emcDvbShift;
|
||||
// advanced config
|
||||
u32 t1_tRCD;
|
||||
u32 t2_tRP;
|
||||
u32 t3_tRAS;
|
||||
u32 t4_tRRD;
|
||||
u32 t5_tRFC;
|
||||
u32 t6_tRTW;
|
||||
u32 t7_tWTR;
|
||||
u32 t8_tREFI;
|
||||
|
||||
u32 mem_burst_read_latency;
|
||||
u32 mem_burst_write_latency;
|
||||
|
||||
u32 eristaCpuUV;
|
||||
u32 eristaCpuVmin;
|
||||
u32 eristaCpuMaxVolt;
|
||||
u32 eristaCpuUnlock;
|
||||
|
||||
u32 marikoCpuUVLow;
|
||||
u32 marikoCpuUVHigh;
|
||||
u32 tableConf;
|
||||
u32 marikoCpuLowVmin;
|
||||
u32 marikoCpuHighVmin;
|
||||
u32 marikoCpuMaxVolt;
|
||||
u32 marikoCpuMaxClock;
|
||||
|
||||
u32 eristaCpuBoostClock;
|
||||
u32 marikoCpuBoostClock;
|
||||
|
||||
u32 eristaGpuUV;
|
||||
u32 eristaGpuVmin;
|
||||
|
||||
u32 marikoGpuUV;
|
||||
u32 marikoGpuVmin;
|
||||
u32 marikoGpuVmax;
|
||||
|
||||
u32 commonGpuVoltOffset;
|
||||
|
||||
u32 gpuSpeedo;
|
||||
|
||||
u32 eristaGpuVoltArray[27];
|
||||
u32 marikoGpuVoltArray[24];
|
||||
|
||||
u32 t6_tRTW_fine_tune;
|
||||
u32 t7_tWTR_fine_tune;
|
||||
|
||||
u32 reserved[60];
|
||||
} CustomizeTable;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define CUST_MAGIC "CUST"
|
||||
#define CUST_MAGIC_LEN 4
|
||||
|
||||
typedef struct {
|
||||
FILE* file;
|
||||
long offset;
|
||||
CustomizeTable cached_table;
|
||||
bool has_cache;
|
||||
} CustHandle;
|
||||
|
||||
static inline bool cust_find_offset(FILE* f, long* out_offset) {
|
||||
u8 buf[512];
|
||||
long pos = 0;
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
while (1) {
|
||||
size_t r = fread(buf, 1, sizeof(buf), f);
|
||||
if (r < CUST_MAGIC_LEN) break;
|
||||
|
||||
for (size_t i = 0; i <= r - CUST_MAGIC_LEN; i++) {
|
||||
if (memcmp(&buf[i], CUST_MAGIC, CUST_MAGIC_LEN) == 0) {
|
||||
*out_offset = pos + (long)i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
pos += (long)(r - (CUST_MAGIC_LEN - 1));
|
||||
fseek(f, pos, SEEK_SET);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cust_read_table(const char* path, CustomizeTable* out) {
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) return false;
|
||||
|
||||
long off;
|
||||
if (!cust_find_offset(f, &off)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
|
||||
if (off + (long)sizeof(CustomizeTable) > size) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, off, SEEK_SET);
|
||||
bool ok = fread(out, 1, sizeof(CustomizeTable), f) == sizeof(CustomizeTable);
|
||||
fclose(f);
|
||||
|
||||
return ok && memcmp(out->cust, CUST_MAGIC, CUST_MAGIC_LEN) == 0;
|
||||
}
|
||||
|
||||
static inline bool cust_write_table(const char* path, const CustomizeTable* in) {
|
||||
FILE* f = fopen(path, "r+b");
|
||||
if (!f) return false;
|
||||
|
||||
long off;
|
||||
if (!cust_find_offset(f, &off)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
|
||||
if (off + (long)sizeof(CustomizeTable) > size) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(f, off, SEEK_SET);
|
||||
bool ok = fwrite(in, 1, sizeof(CustomizeTable), f) == sizeof(CustomizeTable);
|
||||
fflush(f);
|
||||
fclose(f);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static inline bool cust_read_and_cache(const char* path, CustomizeTable* out) {
|
||||
return cust_read_table(path, out);
|
||||
}
|
||||
|
||||
#define CUST_WRITE_FIELD_BATCH(table, field, val) \
|
||||
do { \
|
||||
(table)->field = (val); \
|
||||
} while (0)
|
||||
|
||||
#define CUST_WRITE_FIELD(path, field, val) \
|
||||
do { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(path, &t)) return false; \
|
||||
t.field = (val); \
|
||||
return cust_write_table(path, &t); \
|
||||
} while (0)
|
||||
|
||||
static inline bool cust_set_cust_rev(const char* p, u32 v) { CUST_WRITE_FIELD(p, custRev, v); }
|
||||
// static inline bool cust_set_mtc_conf(const char* p, u32 v) { CUST_WRITE_FIELD(p, mtcConf, v); }
|
||||
static inline bool cust_set_hp_mode(const char* p, u32 v) { CUST_WRITE_FIELD(p, hpMode, v); }
|
||||
|
||||
static inline bool cust_set_common_emc_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, commonEmcMemVolt, v); }
|
||||
static inline bool cust_set_erista_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock, v); }
|
||||
static inline bool cust_set_erista_emc_max1(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock1, v); }
|
||||
static inline bool cust_set_erista_emc_max2(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock2, v); }
|
||||
static inline bool cust_set_mariko_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcMaxClock, v); }
|
||||
static inline bool cust_set_mariko_emc_vddq(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcVddqVolt, v); }
|
||||
static inline bool cust_set_emc_dvb_shift(const char* p, u32 v) { CUST_WRITE_FIELD(p, emcDvbShift, v); }
|
||||
|
||||
static inline bool cust_set_tRCD(const char* p, u32 v) { CUST_WRITE_FIELD(p, t1_tRCD, v); }
|
||||
static inline bool cust_set_tRP(const char* p, u32 v) { CUST_WRITE_FIELD(p, t2_tRP, v); }
|
||||
static inline bool cust_set_tRAS(const char* p, u32 v) { CUST_WRITE_FIELD(p, t3_tRAS, v); }
|
||||
static inline bool cust_set_tRRD(const char* p, u32 v) { CUST_WRITE_FIELD(p, t4_tRRD, v); }
|
||||
static inline bool cust_set_tRFC(const char* p, u32 v) { CUST_WRITE_FIELD(p, t5_tRFC, v); }
|
||||
static inline bool cust_set_tRTW(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW, v); }
|
||||
static inline bool cust_set_tWTR(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR, v); }
|
||||
static inline bool cust_set_tREFI(const char* p, u32 v) { CUST_WRITE_FIELD(p, t8_tREFI, v); }
|
||||
static inline bool cust_set_tRTW_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW_fine_tune, v); }
|
||||
static inline bool cust_set_tWTR_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR_fine_tune, v); }
|
||||
static inline bool cust_set_burst_read_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_read_latency, v); }
|
||||
static inline bool cust_set_burst_write_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_write_latency, v); }
|
||||
|
||||
static inline bool cust_set_erista_cpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuUV, v); }
|
||||
static inline bool cust_set_eristaCpuVmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuVmin, v); }
|
||||
static inline bool cust_set_erista_cpu_max_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuMaxVolt, v); }
|
||||
static inline bool cust_set_eristaCpuUnlock(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuUnlock, v); }
|
||||
|
||||
static inline bool cust_set_mariko_cpu_uv_low(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuUVLow, v); }
|
||||
static inline bool cust_set_mariko_cpu_uv_high(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuUVHigh, v); }
|
||||
static inline bool cust_set_mariko_cpu_low_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuLowVmin, v); }
|
||||
static inline bool cust_set_mariko_cpu_high_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuHighVmin, v); }
|
||||
static inline bool cust_set_mariko_cpu_max_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuMaxVolt, v); }
|
||||
static inline bool cust_set_erista_cpu_boost(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuBoostClock, v); }
|
||||
static inline bool cust_set_mariko_cpu_boost(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuBoostClock, v); }
|
||||
|
||||
static inline bool cust_set_erista_gpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaGpuUV, v); }
|
||||
static inline bool cust_set_erista_gpu_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaGpuVmin, v); }
|
||||
static inline bool cust_set_mariko_gpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuUV, v); }
|
||||
static inline bool cust_set_mariko_gpu_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuVmin, v); }
|
||||
static inline bool cust_set_mariko_gpu_vmax(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuVmax, v); }
|
||||
static inline bool cust_set_common_gpu_offset(const char* p, u32 v) { CUST_WRITE_FIELD(p, commonGpuVoltOffset, v); }
|
||||
static inline bool cust_set_gpu_speedo(const char* p, u32 v) { CUST_WRITE_FIELD(p, gpuSpeedo, v); }
|
||||
static inline bool cust_set_marikoCpuMaxClock(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuMaxClock, v); }
|
||||
|
||||
/* GPU VOLT ARRAY HELPERS */
|
||||
static inline bool cust_set_erista_gpu_volt(const char* p, int idx, u32 v) {
|
||||
if (idx < 0 || idx >= 27) return false;
|
||||
CustomizeTable t;
|
||||
if (!cust_read_table(p, &t)) return false;
|
||||
t.eristaGpuVoltArray[idx] = v;
|
||||
return cust_write_table(p, &t);
|
||||
}
|
||||
|
||||
static inline bool cust_set_mariko_gpu_volt(const char* p, int idx, u32 v) {
|
||||
if (idx < 0 || idx >= 24) return false;
|
||||
CustomizeTable t;
|
||||
if (!cust_read_table(p, &t)) return false;
|
||||
t.marikoGpuVoltArray[idx] = v;
|
||||
return cust_write_table(p, &t);
|
||||
}
|
||||
|
||||
static inline u32 cust_get_field(const CustomizeTable* t, u32 offset) {
|
||||
if (!t) return 0;
|
||||
return *(u32*)((u8*)t + offset);
|
||||
}
|
||||
|
||||
#define CUST_GET_FIELD(table, field) ((table) ? (table)->field : 0)
|
||||
|
||||
static inline u32 cust_get_cust_rev(const CustomizeTable* t) { return CUST_GET_FIELD(t, custRev); }
|
||||
// static inline u32 cust_get_mtc_conf(const CustomizeTable* t) { return CUST_GET_FIELD(t, mtcConf); }
|
||||
static inline u32 cust_get_hp_mode(const CustomizeTable* t) { return CUST_GET_FIELD(t, hpMode); }
|
||||
|
||||
static inline u32 cust_get_common_emc_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, commonEmcMemVolt); }
|
||||
static inline u32 cust_get_erista_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock); }
|
||||
static inline u32 cust_get_erista_emc_max1(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock1); }
|
||||
static inline u32 cust_get_erista_emc_max2(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock2); }
|
||||
static inline u32 cust_get_mariko_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcMaxClock); }
|
||||
static inline u32 cust_get_mariko_emc_vddq(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcVddqVolt); }
|
||||
static inline u32 cust_get_emc_dvb_shift(const CustomizeTable* t) { return CUST_GET_FIELD(t, emcDvbShift); }
|
||||
|
||||
static inline u32 cust_get_tRCD(const CustomizeTable* t) { return CUST_GET_FIELD(t, t1_tRCD); }
|
||||
static inline u32 cust_get_tRP(const CustomizeTable* t) { return CUST_GET_FIELD(t, t2_tRP); }
|
||||
static inline u32 cust_get_tRAS(const CustomizeTable* t) { return CUST_GET_FIELD(t, t3_tRAS); }
|
||||
static inline u32 cust_get_tRRD(const CustomizeTable* t) { return CUST_GET_FIELD(t, t4_tRRD); }
|
||||
static inline u32 cust_get_tRFC(const CustomizeTable* t) { return CUST_GET_FIELD(t, t5_tRFC); }
|
||||
static inline u32 cust_get_tRTW(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW); }
|
||||
static inline u32 cust_get_tWTR(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR); }
|
||||
static inline u32 cust_get_tREFI(const CustomizeTable* t) { return CUST_GET_FIELD(t, t8_tREFI); }
|
||||
static inline u32 cust_get_tRTW_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW_fine_tune); }
|
||||
static inline u32 cust_get_tWTR_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR_fine_tune); }
|
||||
static inline u32 cust_get_burst_read_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_read_latency); }
|
||||
static inline u32 cust_get_burst_write_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_write_latency); }
|
||||
|
||||
static inline u32 cust_get_erista_cpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuUV); }
|
||||
static inline u32 cust_get_eristaCpuVmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuVmin); }
|
||||
static inline u32 cust_get_erista_cpu_max_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuMaxVolt); }
|
||||
static inline u32 cust_get_eristaCpuUnlock(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuUnlock); }
|
||||
|
||||
static inline u32 cust_get_mariko_cpu_uv_low(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuUVLow); }
|
||||
static inline u32 cust_get_mariko_cpu_uv_high(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuUVHigh); }
|
||||
static inline u32 cust_get_mariko_cpu_low_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuLowVmin); }
|
||||
static inline u32 cust_get_mariko_cpu_high_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuHighVmin); }
|
||||
static inline u32 cust_get_mariko_cpu_max_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuMaxVolt); }
|
||||
static inline u32 cust_get_erista_cpu_boost(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuBoostClock); }
|
||||
static inline u32 cust_get_mariko_cpu_boost(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuBoostClock); }
|
||||
static inline u32 cust_get_table_conf(const CustomizeTable* t) { return CUST_GET_FIELD(t, tableConf); }
|
||||
|
||||
static inline u32 cust_get_erista_gpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaGpuUV); }
|
||||
static inline u32 cust_get_erista_gpu_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaGpuVmin); }
|
||||
static inline u32 cust_get_mariko_gpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuUV); }
|
||||
static inline u32 cust_get_mariko_gpu_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuVmin); }
|
||||
static inline u32 cust_get_mariko_gpu_vmax(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuVmax); }
|
||||
static inline u32 cust_get_common_gpu_offset(const CustomizeTable* t) { return CUST_GET_FIELD(t, commonGpuVoltOffset); }
|
||||
static inline u32 cust_get_gpu_speedo(const CustomizeTable* t) { return CUST_GET_FIELD(t, gpuSpeedo); }
|
||||
static inline u32 cust_get_marikoCpuMaxClock(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuMaxClock); }
|
||||
|
||||
static inline u32 cust_get_erista_gpu_volt(const CustomizeTable* t, int idx) {
|
||||
if (!t || idx < 0 || idx >= 27) return 0;
|
||||
return t->eristaGpuVoltArray[idx];
|
||||
}
|
||||
|
||||
static inline u32 cust_get_mariko_gpu_volt(const CustomizeTable* t, int idx) {
|
||||
if (!t || idx < 0 || idx >= 24) return 0;
|
||||
return t->marikoGpuVoltArray[idx];
|
||||
}
|
||||
|
||||
#define DECL_ERISTA_GPU_VOLT_HELPER(freq, idx) \
|
||||
static inline bool cust_set_erista_gpu_volt_##freq( \
|
||||
const char* p, u32 v) { \
|
||||
return cust_set_erista_gpu_volt(p, idx, v); \
|
||||
}
|
||||
|
||||
#define DECL_MARIKO_GPU_VOLT_HELPER(freq, idx) \
|
||||
static inline bool cust_set_mariko_gpu_volt_##freq( \
|
||||
const char* p, u32 v) { \
|
||||
return cust_set_mariko_gpu_volt(p, idx, v); \
|
||||
}
|
||||
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(76800, 0)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(115200, 1)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(153600, 2)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(192000, 3)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(230400, 4)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(268800, 5)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(307200, 6)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(345600, 7)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(384000, 8)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(422400, 9)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(460800, 10)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(499200, 11)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(537600, 12)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(576000, 13)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(614400, 14)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(652800, 15)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(691200, 16)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(729600, 17)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(768000, 18)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(806400, 19)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(844800, 20)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(883200, 21)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(921600, 22)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(960000, 23)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(998400, 24)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(1036800, 25)
|
||||
DECL_ERISTA_GPU_VOLT_HELPER(1075200, 26)
|
||||
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(76800, 0)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(153600, 1)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(230400, 2)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(307200, 3)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(384000, 4)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(460800, 5)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(537600, 6)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(614400, 7)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(691200, 8)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(768000, 9)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(844800, 10)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(921600, 11)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(998400, 12)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1075200, 13)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1152000, 14)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1228800, 15)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1267200, 16)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1305600, 17)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1344000, 18)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1382400, 19)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1420800, 20)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1459200, 21)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1497600, 22)
|
||||
DECL_MARIKO_GPU_VOLT_HELPER(1536000, 23)
|
||||
|
||||
#define DECL_ERISTA_GPU_VOLT_GET(freq, idx) \
|
||||
static inline u32 cust_get_erista_gpu_volt_##freq##_val(const char* p) { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(p, &t)) return 0; \
|
||||
return cust_get_erista_gpu_volt(&t, idx); \
|
||||
}
|
||||
#define DECL_MARIKO_GPU_VOLT_GET(freq, idx) \
|
||||
static inline u32 cust_get_mariko_gpu_volt_##freq##_val(const char* p) { \
|
||||
CustomizeTable t; \
|
||||
if (!cust_read_table(p, &t)) return 0; \
|
||||
return cust_get_mariko_gpu_volt(&t, idx); \
|
||||
}
|
||||
|
||||
DECL_ERISTA_GPU_VOLT_GET(76800, 0)
|
||||
DECL_ERISTA_GPU_VOLT_GET(115200, 1)
|
||||
DECL_ERISTA_GPU_VOLT_GET(153600, 2)
|
||||
DECL_ERISTA_GPU_VOLT_GET(192000, 3)
|
||||
DECL_ERISTA_GPU_VOLT_GET(230400, 4)
|
||||
DECL_ERISTA_GPU_VOLT_GET(268800, 5)
|
||||
DECL_ERISTA_GPU_VOLT_GET(307200, 6)
|
||||
DECL_ERISTA_GPU_VOLT_GET(345600, 7)
|
||||
DECL_ERISTA_GPU_VOLT_GET(384000, 8)
|
||||
DECL_ERISTA_GPU_VOLT_GET(422400, 9)
|
||||
DECL_ERISTA_GPU_VOLT_GET(460800, 10)
|
||||
DECL_ERISTA_GPU_VOLT_GET(499200, 11)
|
||||
DECL_ERISTA_GPU_VOLT_GET(537600, 12)
|
||||
DECL_ERISTA_GPU_VOLT_GET(576000, 13)
|
||||
DECL_ERISTA_GPU_VOLT_GET(614400, 14)
|
||||
DECL_ERISTA_GPU_VOLT_GET(652800, 15)
|
||||
DECL_ERISTA_GPU_VOLT_GET(691200, 16)
|
||||
DECL_ERISTA_GPU_VOLT_GET(729600, 17)
|
||||
DECL_ERISTA_GPU_VOLT_GET(768000, 18)
|
||||
DECL_ERISTA_GPU_VOLT_GET(806400, 19)
|
||||
DECL_ERISTA_GPU_VOLT_GET(844800, 20)
|
||||
DECL_ERISTA_GPU_VOLT_GET(883200, 21)
|
||||
DECL_ERISTA_GPU_VOLT_GET(921600, 22)
|
||||
DECL_ERISTA_GPU_VOLT_GET(960000, 23)
|
||||
DECL_ERISTA_GPU_VOLT_GET(998400, 24)
|
||||
DECL_ERISTA_GPU_VOLT_GET(1036800, 25)
|
||||
DECL_ERISTA_GPU_VOLT_GET(1075200, 26)
|
||||
|
||||
DECL_MARIKO_GPU_VOLT_GET(76800, 0)
|
||||
DECL_MARIKO_GPU_VOLT_GET(153600, 1)
|
||||
DECL_MARIKO_GPU_VOLT_GET(230400, 2)
|
||||
DECL_MARIKO_GPU_VOLT_GET(307200, 3)
|
||||
DECL_MARIKO_GPU_VOLT_GET(384000, 4)
|
||||
DECL_MARIKO_GPU_VOLT_GET(460800, 5)
|
||||
DECL_MARIKO_GPU_VOLT_GET(537600, 6)
|
||||
DECL_MARIKO_GPU_VOLT_GET(614400, 7)
|
||||
DECL_MARIKO_GPU_VOLT_GET(691200, 8)
|
||||
DECL_MARIKO_GPU_VOLT_GET(768000, 9)
|
||||
DECL_MARIKO_GPU_VOLT_GET(844800, 10)
|
||||
DECL_MARIKO_GPU_VOLT_GET(921600, 11)
|
||||
DECL_MARIKO_GPU_VOLT_GET(998400, 12)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1075200, 13)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1152000, 14)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1228800, 15)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1267200, 16)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1305600, 17)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1344000, 18)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1382400, 19)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1420800, 20)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1459200, 21)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1497600, 22)
|
||||
DECL_MARIKO_GPU_VOLT_GET(1536000, 23)
|
||||
|
||||
void SetKipData();
|
||||
void GetKipData();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
Result QueryMemoryMapping(u64* virtaddr, u64 physaddr, u64 size);
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#include "process_management.hpp"
|
||||
#include "file_utils.hpp"
|
||||
#include "errors.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace processManagement {
|
||||
|
||||
namespace {
|
||||
constexpr u64 Qlaunch = 0x0100000000001000ULL;
|
||||
constexpr u32 IsQlaunch = 0x20f;
|
||||
Service pdmqryClone;
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
Result rc = 0;
|
||||
|
||||
rc = pmdmntInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pmdmntInitialize");
|
||||
|
||||
rc = pminfoInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pminfoInitialize");
|
||||
|
||||
rc = pdmqryInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pdmqryInitialize");
|
||||
|
||||
Service* pdmqrySrv = pdmqryGetServiceSession();
|
||||
serviceClone(pdmqrySrv, &pdmqryClone);
|
||||
serviceClose(pdmqrySrv);
|
||||
memcpy(pdmqrySrv, &pdmqryClone, sizeof(Service));
|
||||
|
||||
}
|
||||
|
||||
void WaitForQLaunch() {
|
||||
Result rc = 0;
|
||||
u64 pid = 0;
|
||||
do {
|
||||
rc = pmdmntGetProcessId(&pid, Qlaunch);
|
||||
svcSleepThread(50 * 1000000ULL); // 50ms
|
||||
} while (R_FAILED(rc));
|
||||
}
|
||||
|
||||
// Ty to Masa for this function!
|
||||
Result isApplicationOutOfFocus(bool* outOfFocus) {
|
||||
static s32 last_total_entries = 0;
|
||||
static bool isOutOfFocus = false;
|
||||
s32 total_entries = 0;
|
||||
s32 start_entry_index = 0;
|
||||
s32 end_entry_index = 0;
|
||||
u64 TIDnow;
|
||||
u64 PIDnow;
|
||||
|
||||
Result rc = pmdmntGetApplicationProcessId(&PIDnow);
|
||||
if(R_FAILED(rc)) return rc;
|
||||
rc = pmdmntGetProgramId(&TIDnow, PIDnow);
|
||||
if(R_FAILED(rc)) return rc;
|
||||
|
||||
rc = pdmqryGetAvailablePlayEventRange(&total_entries, &start_entry_index, &end_entry_index);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
if (total_entries == last_total_entries) {
|
||||
*outOfFocus = isOutOfFocus;
|
||||
return 0;
|
||||
}
|
||||
last_total_entries = total_entries;
|
||||
|
||||
PdmPlayEvent events[16];
|
||||
s32 out = 0;
|
||||
s32 start_entry = end_entry_index - 15;
|
||||
if (start_entry < 0) start_entry = 0;
|
||||
rc = pdmqryQueryPlayEvent(start_entry, events, sizeof(events) / sizeof(events[0]), &out);
|
||||
if (R_FAILED(rc)) return rc;
|
||||
if (out == 0) return 1;
|
||||
|
||||
int itr = -1;
|
||||
for (int i = out-1; i >= 0; i--) {
|
||||
if (events[i].play_event_type != PdmPlayEventType_Applet)
|
||||
continue;
|
||||
if (events[i].event_data.applet.applet_id != AppletId_application)
|
||||
continue;
|
||||
union {
|
||||
struct {
|
||||
uint32_t part[2];
|
||||
} parts;
|
||||
uint64_t full;
|
||||
} TID;
|
||||
TID.parts.part[0] = events[i].event_data.applet.program_id[1];
|
||||
TID.parts.part[1] = events[i].event_data.applet.program_id[0];
|
||||
|
||||
|
||||
|
||||
if (TID.full != (TIDnow & ~0xFFF))
|
||||
continue;
|
||||
else {
|
||||
itr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (itr == -1) return 1;
|
||||
|
||||
bool isOut = events[itr].event_data.applet.event_type == PdmAppletEventType_OutOfFocus || events[itr].event_data.applet.event_type == PdmAppletEventType_OutOfFocus4;
|
||||
*outOfFocus = isOut;
|
||||
isOutOfFocus = isOut;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 GetCurrentApplicationId() {
|
||||
Result rc = 0;
|
||||
u64 pid = 0;
|
||||
u64 tid = 0;
|
||||
rc = pmdmntGetApplicationProcessId(&pid);
|
||||
|
||||
if (rc == IsQlaunch) {
|
||||
return Qlaunch;
|
||||
}
|
||||
|
||||
ASSERT_RESULT_OK(rc, "pmdmntGetApplicationProcessId");
|
||||
|
||||
rc = pminfoGetProgramId(&tid, pid);
|
||||
|
||||
if (rc == IsQlaunch) {
|
||||
return Qlaunch;
|
||||
}
|
||||
|
||||
ASSERT_RESULT_OK(rc, "pminfoGetProgramId");
|
||||
|
||||
return tid;
|
||||
}
|
||||
|
||||
void Exit() {
|
||||
pmdmntExit();
|
||||
pminfoExit();
|
||||
pdmqryExit();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
namespace processManagement {
|
||||
|
||||
void Initialize();
|
||||
void WaitForQLaunch();
|
||||
u64 GetCurrentApplicationId();
|
||||
Result isApplicationOutOfFocus(bool* outOfFocus);
|
||||
void Exit();
|
||||
|
||||
}
|
||||
@@ -1,640 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014 - 2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Mikko Perttunen <mperttunen@nvidia.com>
|
||||
*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
#include "soctherm.hpp"
|
||||
#include "board/board.hpp"
|
||||
#include "file_utils.hpp"
|
||||
#include "mem_map.hpp"
|
||||
|
||||
namespace soctherm {
|
||||
|
||||
namespace {
|
||||
|
||||
#define FUSE_CACHE_OFFSET 0x800
|
||||
#define FUSE_TSENSOR_COMMON 0xA80
|
||||
#define CAR_CLK_SOURCE_TSENSOR 0x3B8
|
||||
#define CAR_CLK_OUT_ENB_V 0x360
|
||||
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES 0xC
|
||||
#define SWR_SOC_THERM_RST 1 << 14
|
||||
|
||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB 0x18
|
||||
#define CLK_ENB_SOC_THERM 1 << 14
|
||||
|
||||
#define CAR_CLK_SOURCE_TSENSOR_VAL 0x8000005E
|
||||
|
||||
#define NOMINAL_CALIB_FT 105
|
||||
#define NOMINAL_CALIB_CP 25
|
||||
|
||||
#define THERMCTL_LEVEL0_GROUP_CPU 0x0
|
||||
#define THERMCTL_LEVEL0_GROUP_GPU 0x4
|
||||
#define THERMCTL_LEVEL0_GROUP_MEM 0x8
|
||||
#define THERMCTL_LEVEL0_GROUP_TSENSE 0xc
|
||||
|
||||
#define TEGRA124_SOCTHERM_SENSOR_CPU 0
|
||||
#define TEGRA124_SOCTHERM_SENSOR_MEM 1
|
||||
#define TEGRA124_SOCTHERM_SENSOR_GPU 2
|
||||
#define TEGRA124_SOCTHERM_SENSOR_PLLX 3
|
||||
#define TEGRA124_SOCTHERM_SENSOR_NUM 4
|
||||
|
||||
#define TEGRA_SOCTHERM_THROT_LEVEL_NONE 0
|
||||
#define TEGRA_SOCTHERM_THROT_LEVEL_LOW 1
|
||||
#define TEGRA_SOCTHERM_THROT_LEVEL_MED 2
|
||||
#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 3
|
||||
|
||||
#define SENSOR_CONFIG2 8
|
||||
#define SENSOR_CONFIG2_THERMA_MASK (0xffffu << 16)
|
||||
#define SENSOR_CONFIG2_THERMA_SHIFT 16
|
||||
#define SENSOR_CONFIG2_THERMB_MASK 0xffff
|
||||
#define SENSOR_CONFIG2_THERMB_SHIFT 0
|
||||
|
||||
#define THERMCTL_THERMTRIP_CTL 0x80
|
||||
|
||||
#define SENSOR_PDIV 0x1c0
|
||||
#define SENSOR_PDIV_CPU_MASK (0xf << 12)
|
||||
#define SENSOR_PDIV_GPU_MASK (0xf << 8)
|
||||
#define SENSOR_PDIV_MEM_MASK (0xf << 4)
|
||||
#define SENSOR_PDIV_PLLX_MASK (0xf << 0)
|
||||
|
||||
#define SENSOR_HOTSPOT_OFF 0x1c4
|
||||
#define SENSOR_HOTSPOT_CPU_MASK (0xff << 16)
|
||||
#define SENSOR_HOTSPOT_GPU_MASK (0xff << 8)
|
||||
#define SENSOR_HOTSPOT_MEM_MASK (0xff << 0)
|
||||
|
||||
#define SENSOR_HW_PLLX_OFFSET_EN 0x1e4
|
||||
#define SENSOR_HW_PLLX_OFFSET_MEM_EN_MASK BIT(2)
|
||||
#define SENSOR_HW_PLLX_OFFSET_CPU_EN_MASK BIT(1)
|
||||
#define SENSOR_HW_PLLX_OFFSET_GPU_EN_MASK BIT(0)
|
||||
|
||||
#define SENSOR_HW_PLLX_OFFSET_MIN 0x1e8
|
||||
#define SENSOR_HW_PLLX_OFFSET_MAX 0x1ec
|
||||
#define SENSOR_HW_PLLX_OFFSET_MEM_MASK (0xff << 16)
|
||||
#define SENSOR_HW_PLLX_OFFSET_GPU_MASK (0xff << 8)
|
||||
#define SENSOR_HW_PLLX_OFFSET_CPU_MASK (0xff << 0)
|
||||
|
||||
#define SENSOR_TEMP1 0x1c8
|
||||
#define SENSOR_TEMP1_CPU_TEMP_MASK (0xffffu << 16)
|
||||
#define SENSOR_TEMP1_GPU_TEMP_MASK 0xffff
|
||||
#define SENSOR_TEMP2 0x1cc
|
||||
#define SENSOR_TEMP2_MEM_TEMP_MASK (0xffffu << 16)
|
||||
#define SENSOR_TEMP2_PLLX_TEMP_MASK 0xffff
|
||||
|
||||
#define SENSOR_VALID 0x1e0
|
||||
#define SENSOR_GPU_VALID_MASK BIT(9)
|
||||
#define SENSOR_CPU_VALID_MASK 0xf
|
||||
#define SENSOR_MEM_VALID_MASK (0x3 << 10)
|
||||
|
||||
#define TEGRA210_THERMTRIP_ANY_EN_MASK (0x1u << 31)
|
||||
#define TEGRA210_THERMTRIP_MEM_EN_MASK (0x1 << 30)
|
||||
#define TEGRA210_THERMTRIP_GPU_EN_MASK (0x1 << 29)
|
||||
#define TEGRA210_THERMTRIP_CPU_EN_MASK (0x1 << 28)
|
||||
#define TEGRA210_THERMTRIP_TSENSE_EN_MASK (0x1 << 27)
|
||||
#define TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK (0x1ff << 18)
|
||||
#define TEGRA210_THERMTRIP_CPU_THRESH_MASK (0x1ff << 9)
|
||||
#define TEGRA210_THERMTRIP_TSENSE_THRESH_MASK 0x1ff
|
||||
|
||||
#define TEGRA210_THERM_IRQ_MEM_MASK (0x3 << 24)
|
||||
#define TEGRA210_THERM_IRQ_GPU_MASK (0x3 << 16)
|
||||
#define TEGRA210_THERM_IRQ_CPU_MASK (0x3 << 8)
|
||||
#define TEGRA210_THERM_IRQ_TSENSE_MASK (0x3 << 0)
|
||||
|
||||
#define TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK (0x1ff << 18)
|
||||
#define TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK (0x1ff << 9)
|
||||
|
||||
#define TEGRA210_THRESH_GRAIN 500
|
||||
#define TEGRA210_BPTT 9
|
||||
|
||||
#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff
|
||||
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13)
|
||||
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
|
||||
#define CALIB_COEFFICIENT 1000000LL
|
||||
|
||||
#define SENSOR_CONFIG0 0
|
||||
#define SENSOR_CONFIG0_STOP BIT(0)
|
||||
#define SENSOR_CONFIG0_CPTR_OVER BIT(2)
|
||||
#define SENSOR_CONFIG0_OVER BIT(3)
|
||||
#define SENSOR_CONFIG0_TCALC_OVER BIT(4)
|
||||
#define SENSOR_CONFIG0_TALL_MASK (0xfffff << 8)
|
||||
#define SENSOR_CONFIG0_TALL_SHIFT 8
|
||||
|
||||
#define PDIV_RATE_T210B0 0xCC0C
|
||||
#define PDIV_RATE_T210 0x8888
|
||||
#define HOTSPOT_VAL 0xA0500
|
||||
|
||||
#define SENSOR_CONFIG1 4
|
||||
#define SENSOR_CONFIG1_TSAMPLE_MASK 0x3ff
|
||||
#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0
|
||||
#define SENSOR_CONFIG1_TIDDQ_EN_MASK (0x3f << 15)
|
||||
#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15
|
||||
#define SENSOR_CONFIG1_TEN_COUNT_MASK (0x3f << 24)
|
||||
#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24
|
||||
#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31)
|
||||
|
||||
#define READBACK_VALUE_MASK 0xff00
|
||||
#define READBACK_VALUE_SHIFT 8
|
||||
#define READBACK_ADD_HALF BIT(7)
|
||||
#define READBACK_NEGATE BIT(0)
|
||||
|
||||
#define PDIV_MASK_T210B0 0xFFFF00F0
|
||||
#define HOTSPOT_MASK_T210B0 0xFF0000FF
|
||||
|
||||
#define PDIV_MASK_T210 0xFFFF0000
|
||||
#define HOTSPOT_MASK_T210 0xFF000000
|
||||
|
||||
#define TSENSOR_TSENSOR_CLKEN 0x1DC
|
||||
#define TSENSOR_TSENSOR_ENABLE 225
|
||||
|
||||
const TSensorFuse tfuse = {
|
||||
.fuse_base_cp_mask = 0x3ff << 11,
|
||||
.fuse_base_cp_shift = 11,
|
||||
.fuse_base_ft_mask = 0x7ffu << 21,
|
||||
.fuse_base_ft_shift = 21,
|
||||
.fuse_shift_ft_mask = 0x1f << 6,
|
||||
.fuse_shift_ft_shift = 6,
|
||||
.fuse_spare_realignment = 0,
|
||||
};
|
||||
|
||||
const TSensorConfig eristaConf = {
|
||||
.tall = 16300,
|
||||
.tiddq_en = 1,
|
||||
.ten_count = 1,
|
||||
.pdiv = 8,
|
||||
.pdiv_ate = 8,
|
||||
.tsample = 120,
|
||||
.tsample_ate = 480,
|
||||
};
|
||||
|
||||
const TSensorConfig marikoConf = {
|
||||
.tall = 16300,
|
||||
.tiddq_en = 1,
|
||||
.ten_count = 1,
|
||||
.pdiv = 12,
|
||||
.pdiv_ate = 6,
|
||||
.tsample = 240,
|
||||
.tsample_ate = 480,
|
||||
};
|
||||
|
||||
const struct TSensorGroup tSensorGroupCpu = {
|
||||
.name = "cpu",
|
||||
.id = TEGRA124_SOCTHERM_SENSOR_CPU,
|
||||
.sensor_temp_offset = SENSOR_TEMP1,
|
||||
.sensor_temp_mask = SENSOR_TEMP1_CPU_TEMP_MASK,
|
||||
.pdiv_mask = SENSOR_PDIV_CPU_MASK,
|
||||
.pllx_hotspot_diff = 10,
|
||||
.pllx_hotspot_mask = SENSOR_HOTSPOT_CPU_MASK,
|
||||
.hw_pllx_offset_mask = SENSOR_HW_PLLX_OFFSET_CPU_MASK,
|
||||
.hw_pllx_offset_en_mask = SENSOR_HW_PLLX_OFFSET_CPU_EN_MASK,
|
||||
.thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK,
|
||||
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
|
||||
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK,
|
||||
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
|
||||
.thermctl_isr_mask = TEGRA210_THERM_IRQ_CPU_MASK,
|
||||
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
|
||||
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
|
||||
};
|
||||
|
||||
const struct TSensorGroup tSensorGroupGpu = {
|
||||
.name = "gpu",
|
||||
.id = TEGRA124_SOCTHERM_SENSOR_GPU,
|
||||
.sensor_temp_offset = SENSOR_TEMP1,
|
||||
.sensor_temp_mask = SENSOR_TEMP1_GPU_TEMP_MASK,
|
||||
.pdiv_mask = SENSOR_PDIV_GPU_MASK,
|
||||
.pllx_hotspot_diff = 5,
|
||||
.pllx_hotspot_mask = SENSOR_HOTSPOT_GPU_MASK,
|
||||
.hw_pllx_offset_mask = SENSOR_HW_PLLX_OFFSET_GPU_MASK,
|
||||
.hw_pllx_offset_en_mask = SENSOR_HW_PLLX_OFFSET_GPU_EN_MASK,
|
||||
.thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK,
|
||||
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
|
||||
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
|
||||
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
|
||||
.thermctl_isr_mask = TEGRA210_THERM_IRQ_GPU_MASK,
|
||||
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
|
||||
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
|
||||
};
|
||||
|
||||
const struct TSensorGroup tSensorGroupPll = {
|
||||
.name = "pll",
|
||||
.id = TEGRA124_SOCTHERM_SENSOR_PLLX,
|
||||
.sensor_temp_offset = SENSOR_TEMP2,
|
||||
.sensor_temp_mask = SENSOR_TEMP2_PLLX_TEMP_MASK,
|
||||
.pdiv_mask = SENSOR_PDIV_PLLX_MASK,
|
||||
.thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK,
|
||||
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
|
||||
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK,
|
||||
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
|
||||
.thermctl_isr_mask = TEGRA210_THERM_IRQ_TSENSE_MASK,
|
||||
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
|
||||
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
|
||||
};
|
||||
|
||||
const struct TSensorGroup eristaTSensorGroupMem = {
|
||||
.name = "mem",
|
||||
.id = TEGRA124_SOCTHERM_SENSOR_MEM,
|
||||
.sensor_temp_offset = SENSOR_TEMP2,
|
||||
.sensor_temp_mask = SENSOR_TEMP2_MEM_TEMP_MASK,
|
||||
.pdiv_mask = SENSOR_PDIV_MEM_MASK,
|
||||
.pllx_hotspot_diff = 0,
|
||||
.pllx_hotspot_mask = SENSOR_HOTSPOT_MEM_MASK,
|
||||
.hw_pllx_offset_mask = SENSOR_HW_PLLX_OFFSET_MEM_MASK,
|
||||
.hw_pllx_offset_en_mask = SENSOR_HW_PLLX_OFFSET_MEM_EN_MASK,
|
||||
.thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK,
|
||||
.thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
|
||||
.thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
|
||||
.thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
|
||||
.thermctl_isr_mask = TEGRA210_THERM_IRQ_MEM_MASK,
|
||||
.thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
|
||||
.thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
|
||||
};
|
||||
|
||||
const TSensor eristaTSensors[] = {
|
||||
{
|
||||
.name = "cpu0",
|
||||
.base = 0xc0,
|
||||
.config = &eristaConf,
|
||||
.calib_fuse_offset = 0x198,
|
||||
.fuse_corr = {
|
||||
.alpha = 1085000,
|
||||
.beta = 3244200,
|
||||
},
|
||||
.group = &tSensorGroupCpu,
|
||||
}, {
|
||||
.name = "cpu1",
|
||||
.base = 0xe0,
|
||||
.config = &eristaConf,
|
||||
.calib_fuse_offset = 0x184,
|
||||
.fuse_corr = {
|
||||
.alpha = 1126200,
|
||||
.beta = -67500,
|
||||
},
|
||||
.group = &tSensorGroupCpu,
|
||||
}, {
|
||||
.name = "cpu2",
|
||||
.base = 0x100,
|
||||
.config = &eristaConf,
|
||||
.calib_fuse_offset = 0x188,
|
||||
.fuse_corr = {
|
||||
.alpha = 1098400,
|
||||
.beta = 2251100,
|
||||
},
|
||||
.group = &tSensorGroupCpu,
|
||||
}, {
|
||||
.name = "cpu3",
|
||||
.base = 0x120,
|
||||
.config = &eristaConf,
|
||||
.calib_fuse_offset = 0x22c,
|
||||
.fuse_corr = {
|
||||
.alpha = 1108000,
|
||||
.beta = 602700,
|
||||
},
|
||||
.group = &tSensorGroupCpu,
|
||||
}, {
|
||||
.name = "gpu",
|
||||
.base = 0x180,
|
||||
.config = &eristaConf,
|
||||
.calib_fuse_offset = 0x254,
|
||||
.fuse_corr = {
|
||||
.alpha = 1074300,
|
||||
.beta = 2734900,
|
||||
},
|
||||
.group = &tSensorGroupGpu,
|
||||
}, {
|
||||
.name = "pllx",
|
||||
.base = 0x1a0,
|
||||
.config = &eristaConf,
|
||||
.calib_fuse_offset = 0x260,
|
||||
.fuse_corr = {
|
||||
.alpha = 1039700,
|
||||
.beta = 6829100,
|
||||
},
|
||||
.group = &tSensorGroupPll,
|
||||
}, {
|
||||
.name = "mem0",
|
||||
.base = 0x140,
|
||||
.config = &eristaConf,
|
||||
.calib_fuse_offset = 0x258,
|
||||
.fuse_corr = {
|
||||
.alpha = 1069200,
|
||||
.beta = 3549900,
|
||||
},
|
||||
.group = &eristaTSensorGroupMem,
|
||||
}, {
|
||||
.name = "mem1",
|
||||
.base = 0x160,
|
||||
.config = &eristaConf,
|
||||
.calib_fuse_offset = 0x25c,
|
||||
.fuse_corr = {
|
||||
.alpha = 1173700,
|
||||
.beta = -6263600,
|
||||
},
|
||||
.group = &eristaTSensorGroupMem,
|
||||
},
|
||||
};
|
||||
|
||||
const TSensor marikoTSensors[] = {
|
||||
{
|
||||
.name = "cpu0",
|
||||
.base = 0xc0,
|
||||
.config = &marikoConf,
|
||||
.calib_fuse_offset = 0x198,
|
||||
.fuse_corr = {
|
||||
.alpha = 1085000,
|
||||
.beta = 3244200,
|
||||
},
|
||||
.group = &tSensorGroupCpu,
|
||||
}, {
|
||||
.name = "cpu1",
|
||||
.base = 0xe0,
|
||||
.config = &marikoConf,
|
||||
.calib_fuse_offset = 0x184,
|
||||
.fuse_corr = {
|
||||
.alpha = 1126200,
|
||||
.beta = -67500,
|
||||
},
|
||||
.group = &tSensorGroupCpu,
|
||||
}, {
|
||||
.name = "cpu2",
|
||||
.base = 0x100,
|
||||
.config = &marikoConf,
|
||||
.calib_fuse_offset = 0x188,
|
||||
.fuse_corr = {
|
||||
.alpha = 1098400,
|
||||
.beta = 2251100,
|
||||
},
|
||||
.group = &tSensorGroupCpu,
|
||||
}, {
|
||||
.name = "cpu3",
|
||||
.base = 0x120,
|
||||
.config = &marikoConf,
|
||||
.calib_fuse_offset = 0x22c,
|
||||
.fuse_corr = {
|
||||
.alpha = 1108000,
|
||||
.beta = 602700,
|
||||
},
|
||||
.group = &tSensorGroupCpu,
|
||||
}, {
|
||||
.name = "gpu",
|
||||
.base = 0x180,
|
||||
.config = &marikoConf,
|
||||
.calib_fuse_offset = 0x254,
|
||||
.fuse_corr = {
|
||||
.alpha = 1074300,
|
||||
.beta = 2734900,
|
||||
},
|
||||
.group = &tSensorGroupGpu,
|
||||
}, {
|
||||
.name = "pllx",
|
||||
.base = 0x1a0,
|
||||
.config = &marikoConf,
|
||||
.calib_fuse_offset = 0x260,
|
||||
.fuse_corr = {
|
||||
.alpha = 1039700,
|
||||
.beta = 6829100,
|
||||
},
|
||||
.group = &tSensorGroupPll,
|
||||
},
|
||||
};
|
||||
|
||||
u32 calib[SocthermTSensor_EnumMax] = {};
|
||||
u64 socthermVa, carVa, fuseVa;
|
||||
bool isMariko;
|
||||
}
|
||||
|
||||
template<typename T = u32>
|
||||
static inline T ReadReg(u64 base, u32 offset) {
|
||||
return *reinterpret_cast<volatile T*>(base + offset);
|
||||
}
|
||||
|
||||
template<typename T = u32>
|
||||
static inline void WriteReg(u64 base, u32 offset, T value) {
|
||||
*reinterpret_cast<volatile T*>(base + offset) = value;
|
||||
}
|
||||
|
||||
template<typename T = u32>
|
||||
static inline void SetBits(u64 base, u32 offset, T mask) {
|
||||
WriteReg(base, offset, ReadReg<T>(base, offset) | mask);
|
||||
}
|
||||
|
||||
template<typename T = u32>
|
||||
static inline void ClearBits(u64 base, u32 offset, T mask) {
|
||||
WriteReg(base, offset, ReadReg<T>(base, offset) & ~mask);
|
||||
}
|
||||
|
||||
Result MapAddress(u64 &va, const u64 &physAddr, const char *name) {
|
||||
Result mapResult = QueryMemoryMapping(&va, physAddr, 0x1000);
|
||||
if (R_FAILED(mapResult)) {
|
||||
fileUtils::LogLine("[Soctherm] Failed to map %s! %u", name, R_DESCRIPTION(mapResult));
|
||||
}
|
||||
|
||||
return mapResult;
|
||||
}
|
||||
|
||||
static inline s32 sign_extend32(u32 value, int index) {
|
||||
u8 shift = 31 - index;
|
||||
return (s32) (value << shift) >> shift;
|
||||
}
|
||||
|
||||
static inline s64 div64_s64(s64 dividend, s64 divisor) {
|
||||
return dividend / divisor;
|
||||
}
|
||||
|
||||
static s64 div64_s64_precise(s64 a, s32 b) {
|
||||
s64 r, al;
|
||||
|
||||
al = a << 16;
|
||||
|
||||
r = div64_s64(al * 2 + 1, 2 * b);
|
||||
return r >> 16;
|
||||
}
|
||||
|
||||
bool IsDisabledThroughSleep() {
|
||||
return (ReadReg(carVa, CLK_RST_CONTROLLER_RST_DEVICES) & SWR_SOC_THERM_RST) || !(ReadReg(carVa, CLK_RST_CONTROLLER_CLK_OUT_ENB) & CLK_ENB_SOC_THERM);
|
||||
}
|
||||
|
||||
bool IsSensorEnabled() {
|
||||
return ReadReg(socthermVa, TSENSOR_TSENSOR_CLKEN);
|
||||
}
|
||||
|
||||
void EnableSensor(const TSensor *sensor, u32 sensorIdx) {
|
||||
u32 val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT;
|
||||
WriteReg(socthermVa, sensor->base + SENSOR_CONFIG0, val);
|
||||
|
||||
val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT;
|
||||
val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT;
|
||||
val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT;
|
||||
val |= SENSOR_CONFIG1_TEMP_ENABLE;
|
||||
WriteReg(socthermVa, sensor->base + SENSOR_CONFIG1, val);
|
||||
WriteReg(socthermVa, sensor->base + SENSOR_CONFIG2, calib[sensorIdx]);
|
||||
}
|
||||
|
||||
s32 TranslateTemp(u16 val) {
|
||||
s32 t;
|
||||
|
||||
t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
|
||||
if (val & READBACK_ADD_HALF) {
|
||||
t += 500;
|
||||
}
|
||||
|
||||
if (val & READBACK_NEGATE) {
|
||||
t *= -1;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void StartSensors() {
|
||||
u32 pdiv, hotspot;
|
||||
|
||||
if (isMariko) {
|
||||
for (u32 i = 0; i < std::size(marikoTSensors); ++i) {
|
||||
EnableSensor(&marikoTSensors[i], i);
|
||||
}
|
||||
|
||||
pdiv = (ReadReg(socthermVa, SENSOR_PDIV) & PDIV_MASK_T210B0) | PDIV_RATE_T210B0;
|
||||
hotspot = (ReadReg(socthermVa, SENSOR_HOTSPOT_OFF) & HOTSPOT_MASK_T210B0) | HOTSPOT_VAL;
|
||||
} else {
|
||||
for (u32 i = 0; i < std::size(eristaTSensors); ++i) {
|
||||
EnableSensor(&eristaTSensors[i], i);
|
||||
}
|
||||
|
||||
pdiv = (ReadReg(socthermVa, SENSOR_PDIV) & PDIV_MASK_T210) | PDIV_RATE_T210;
|
||||
hotspot = (ReadReg(socthermVa, SENSOR_HOTSPOT_OFF) & HOTSPOT_MASK_T210) | HOTSPOT_VAL;
|
||||
|
||||
EnableSensor(&eristaTSensors[SocthermTSensor_MEM0], SocthermTSensor_MEM0);
|
||||
EnableSensor(&eristaTSensors[SocthermTSensor_MEM1], SocthermTSensor_MEM1);
|
||||
}
|
||||
|
||||
WriteReg(socthermVa, SENSOR_PDIV, pdiv);
|
||||
WriteReg(socthermVa, SENSOR_HOTSPOT_OFF, hotspot);
|
||||
WriteReg(socthermVa, TSENSOR_TSENSOR_CLKEN, TSENSOR_TSENSOR_ENABLE);
|
||||
}
|
||||
|
||||
void ReadSensors(TSensorTemps &temps) {
|
||||
if (IsDisabledThroughSleep()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsSensorEnabled()) {
|
||||
StartSensors();
|
||||
}
|
||||
|
||||
temps.cpu = TranslateTemp(ReadReg(socthermVa, SENSOR_TEMP1) >> 16);
|
||||
temps.gpu = TranslateTemp(ReadReg(socthermVa, SENSOR_TEMP1) & SENSOR_TEMP1_GPU_TEMP_MASK);
|
||||
temps.pllx = TranslateTemp(ReadReg(socthermVa, SENSOR_TEMP2) & SENSOR_TEMP2_PLLX_TEMP_MASK);
|
||||
|
||||
if (board::GetSocType() == HocClkSocType_Erista) {
|
||||
temps.mem = TranslateTemp(ReadReg(socthermVa, SENSOR_TEMP2) >> 16);
|
||||
} else {
|
||||
temps.mem = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void CalcSharedCal(const TSensorFuse *tfuse, TSensorSharedCalib *shared, u64 fuseVa) {
|
||||
s32 shifted_cp, shifted_ft;
|
||||
|
||||
u32 val = ReadReg(fuseVa, FUSE_TSENSOR_COMMON);
|
||||
|
||||
shared->base_cp = (val & tfuse->fuse_base_cp_mask) >> tfuse->fuse_base_cp_shift;
|
||||
shared->base_ft = (val & tfuse->fuse_base_ft_mask) >> tfuse->fuse_base_ft_shift;
|
||||
|
||||
shifted_ft = (val & tfuse->fuse_shift_ft_mask) >> tfuse->fuse_shift_ft_shift;
|
||||
shifted_ft = sign_extend32(shifted_ft, 4);
|
||||
|
||||
if (tfuse->fuse_spare_realignment) {
|
||||
val = ReadReg(fuseVa, tfuse->fuse_spare_realignment + FUSE_CACHE_OFFSET);
|
||||
}
|
||||
|
||||
shifted_cp = sign_extend32(val, 5);
|
||||
|
||||
shared->actual_temp_cp = 2 * NOMINAL_CALIB_CP + shifted_cp;
|
||||
shared->actual_temp_ft = 2 * NOMINAL_CALIB_FT + shifted_ft;
|
||||
}
|
||||
|
||||
void CalcTSensorCalib(const TSensorConfig *cfg, TSensorSharedCalib *shared, const FuseCorrCoeff *corr, u32 *calibration, u32 offset, u64 fuseVa) {
|
||||
u32 val, calib;
|
||||
s32 actual_tsensor_ft, actual_tsensor_cp;
|
||||
s32 delta_sens, delta_temp;
|
||||
s32 mult, div;
|
||||
s16 therma, thermb;
|
||||
s64 temp;
|
||||
|
||||
val = ReadReg(fuseVa, offset + FUSE_CACHE_OFFSET);
|
||||
|
||||
actual_tsensor_cp = (shared->base_cp * 64) + sign_extend32(val, 12);
|
||||
val = (val & FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK) >> FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT;
|
||||
actual_tsensor_ft = (shared->base_ft * 32) + sign_extend32(val, 12);
|
||||
|
||||
delta_sens = actual_tsensor_ft - actual_tsensor_cp;
|
||||
delta_temp = shared->actual_temp_ft - shared->actual_temp_cp;
|
||||
|
||||
mult = cfg->pdiv * cfg->tsample_ate;
|
||||
div = cfg->tsample * cfg->pdiv_ate;
|
||||
|
||||
temp = (s64)delta_temp * (1LL << 13) * mult;
|
||||
therma = div64_s64_precise(temp, (s64)delta_sens * div);
|
||||
|
||||
temp = ((s64)actual_tsensor_ft * shared->actual_temp_cp) - ((s64)actual_tsensor_cp * shared->actual_temp_ft);
|
||||
thermb = div64_s64_precise(temp, delta_sens);
|
||||
|
||||
temp = (s64)therma * corr->alpha;
|
||||
therma = div64_s64_precise(temp, CALIB_COEFFICIENT);
|
||||
|
||||
temp = (s64)thermb * corr->alpha + corr->beta;
|
||||
thermb = div64_s64_precise(temp, CALIB_COEFFICIENT);
|
||||
|
||||
calib = ((u16)therma << SENSOR_CONFIG2_THERMA_SHIFT) | ((u16)thermb << SENSOR_CONFIG2_THERMB_SHIFT);
|
||||
|
||||
*calibration = calib;
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
isMariko = board::GetSocType() == HocClkSocType_Mariko;
|
||||
|
||||
constexpr u64 SocthermPa = 0x700E2000, FusePa = 0x7000F000, CarPa = 0x60006000;
|
||||
R_UNLESS(MapAddress(socthermVa, SocthermPa, "soctherm"));
|
||||
R_UNLESS(MapAddress( fuseVa, FusePa, "fuse"));
|
||||
R_UNLESS(MapAddress( carVa, CarPa, "car"));
|
||||
|
||||
WriteReg(carVa, CAR_CLK_SOURCE_TSENSOR, CAR_CLK_SOURCE_TSENSOR_VAL);
|
||||
SetBits(carVa, CAR_CLK_OUT_ENB_V, 0x10);
|
||||
svcSleepThread(2000);
|
||||
|
||||
TSensorSharedCalib sharedCal = {};
|
||||
CalcSharedCal(&tfuse, &sharedCal, fuseVa);
|
||||
|
||||
if (isMariko) {
|
||||
for (u32 i = 0; i < std::size(marikoTSensors); ++i) {
|
||||
CalcTSensorCalib(marikoTSensors[i].config, &sharedCal, &marikoTSensors[i].fuse_corr, &calib[i], marikoTSensors[i].calib_fuse_offset, fuseVa);
|
||||
}
|
||||
} else {
|
||||
for (u32 i = 0; i < std::size(eristaTSensors); ++i) {
|
||||
CalcTSensorCalib(eristaTSensors[i].config, &sharedCal, &eristaTSensors[i].fuse_corr, &calib[i], eristaTSensors[i].calib_fuse_offset, fuseVa);
|
||||
}
|
||||
}
|
||||
|
||||
StartSensors();
|
||||
|
||||
fileUtils::LogLine("[Soctherm] Finished init.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2014 - 2019, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Mikko Perttunen <mperttunen@nvidia.com>
|
||||
*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <switch.h>
|
||||
#include <hocclk.h>
|
||||
|
||||
namespace soctherm {
|
||||
|
||||
#define R_UNLESS(rc) \
|
||||
do { \
|
||||
if (R_FAILED(rc)) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct TSensorConfig {
|
||||
u32 tall;
|
||||
u32 tiddq_en;
|
||||
u32 ten_count;
|
||||
u32 pdiv;
|
||||
u32 pdiv_ate;
|
||||
u32 tsample;
|
||||
u32 tsample_ate;
|
||||
};
|
||||
|
||||
struct FuseCorrCoeff {
|
||||
s32 alpha;
|
||||
s32 beta;
|
||||
};
|
||||
|
||||
struct TSensorGroup {
|
||||
const char *name;
|
||||
u8 id;
|
||||
u16 sensor_temp_offset;
|
||||
u32 sensor_temp_mask;
|
||||
u32 pdiv_mask;
|
||||
u32 pllx_hotspot_diff;
|
||||
u32 pllx_hotspot_mask;
|
||||
u32 hw_pllx_offset_mask;
|
||||
u32 hw_pllx_offset_en_mask;
|
||||
u32 thermtrip_enable_mask;
|
||||
u32 thermtrip_any_en_mask;
|
||||
u32 thermtrip_threshold_mask;
|
||||
u16 thermctl_lvl0_offset;
|
||||
u32 thermctl_isr_mask;
|
||||
u32 thermctl_lvl0_up_thresh_mask;
|
||||
u32 thermctl_lvl0_dn_thresh_mask;
|
||||
};
|
||||
|
||||
struct TSensorGroupOffsets {
|
||||
u32 max;
|
||||
u32 min;
|
||||
u32 hw_offsetting_en;
|
||||
const TSensorGroup *ttg;
|
||||
};
|
||||
|
||||
struct TSensor {
|
||||
const char *name;
|
||||
const u32 base;
|
||||
const TSensorConfig *config;
|
||||
const u32 calib_fuse_offset;
|
||||
const FuseCorrCoeff fuse_corr;
|
||||
const TSensorGroup *group;
|
||||
};
|
||||
|
||||
struct TSensorFuse {
|
||||
u32 fuse_base_cp_mask;
|
||||
u32 fuse_base_cp_shift;
|
||||
u32 fuse_base_ft_mask;
|
||||
u32 fuse_base_ft_shift;
|
||||
u32 fuse_shift_ft_mask;
|
||||
u32 fuse_shift_ft_shift;
|
||||
u32 fuse_spare_realignment;
|
||||
};
|
||||
|
||||
struct TSensorSharedCalib {
|
||||
u32 base_cp;
|
||||
u32 base_ft;
|
||||
u32 actual_temp_cp;
|
||||
u32 actual_temp_ft;
|
||||
};
|
||||
|
||||
enum SocthermTSensor : u32 {
|
||||
SocthermTSensor_CPU0 = 0,
|
||||
SocthermTSensor_CPU1 = 1,
|
||||
SocthermTSensor_CPU2 = 2,
|
||||
SocthermTSensor_CPU3 = 3,
|
||||
SocthermTSensor_GPU = 4,
|
||||
SocthermTSensor_PLLX = 5,
|
||||
SocthermTSensor_MEM0 = 6,
|
||||
SocthermTSensor_MEM1 = 7,
|
||||
SocthermTSensor_EnumMax = 8,
|
||||
};
|
||||
|
||||
struct TSensorTemps {
|
||||
s32 cpu;
|
||||
s32 gpu;
|
||||
s32 mem;
|
||||
s32 pllx;
|
||||
};
|
||||
|
||||
void Initialize();
|
||||
void ReadSensors(TSensorTemps &temps);
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user