Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dd9bc0449 | ||
|
|
ced04a3060 | ||
|
|
605f87b078 | ||
|
|
2e083b59a6 | ||
|
|
00f70e7c5e |
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@@ -54,9 +54,6 @@ jobs:
|
||||
echo $SHORT_SHA > dist/.commit
|
||||
echo $GITHUB_SHA >> dist/.commit
|
||||
|
||||
- name: Clone Libnx
|
||||
run: git clone https://github.com/switchbrew/libnx.git
|
||||
|
||||
- name: Clone Atmosphere
|
||||
run: git clone --depth=1 --single-branch https://github.com/Atmosphere-NX/Atmosphere.git atmosphere -b $(cat ams_ver.txt)
|
||||
|
||||
@@ -84,23 +81,14 @@ jobs:
|
||||
ccache --set-config=max_size=10G
|
||||
ccache --set-config=compiler_check=content
|
||||
ccache --zero-stats
|
||||
|
||||
- name: Build Libnx
|
||||
shell: bash
|
||||
run: |
|
||||
export CC="ccache aarch64-none-elf-gcc"
|
||||
export CXX="ccache aarch64-none-elf-g++"
|
||||
pushd libnx
|
||||
make -j$(($(nproc) * 4)) install CXX="ccache aarch64-none-elf-g++" CC="ccache aarch64-none-elf-gcc"
|
||||
popd
|
||||
|
||||
|
||||
- name: Build hoc-clk sysmodule and overlay
|
||||
shell: bash
|
||||
run: |
|
||||
export CC="ccache aarch64-none-elf-gcc"
|
||||
export CXX="ccache aarch64-none-elf-g++"
|
||||
|
||||
ROOT_DIR="$GITHUB_WORKSPACE/Source/hoc-clk"
|
||||
ROOT_DIR="$GITHUB_WORKSPACE/Source/sys-clk"
|
||||
DIST_DIR="$ROOT_DIR/dist"
|
||||
|
||||
mkdir -p "$DIST_DIR"
|
||||
@@ -113,14 +101,13 @@ jobs:
|
||||
echo "TITLE_ID: $TITLE_ID"
|
||||
|
||||
pushd "$ROOT_DIR/sysmodule"
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
make -j$(($(nproc) * 2)) CXX="ccache aarch64-none-elf-g++" CC="ccache aarch64-none-elf-gcc"
|
||||
popd
|
||||
|
||||
mkdir -p "$DIST_DIR/atmosphere/contents/$TITLE_ID/flags"
|
||||
|
||||
cp -vf \
|
||||
"$ROOT_DIR/sysmodule/out/hoc-clk.nsp" \
|
||||
"$ROOT_DIR/sysmodule/out/horizon-oc.nsp" \
|
||||
"$DIST_DIR/atmosphere/contents/$TITLE_ID/exefs.nsp"
|
||||
|
||||
: >"$DIST_DIR/atmosphere/contents/$TITLE_ID/flags/boot2.flag"
|
||||
|
||||
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
|
||||
@@ -1,11 +1,12 @@
|
||||
Horizon OC Compilation Instructions
|
||||
Horizon OC Zeus Compilation Instructions
|
||||
|
||||
1. Install devkitpro (https://devkitpro.org/wiki/Getting_Started) with switch-dev
|
||||
2. Set up a development enviorment for compiling Atmosphere (https://github.com/Atmosphere-NX/Atmosphere/blob/master/docs/building.md)
|
||||
3. Install GNU make and ENSURE THAT YOUR ENVIORMENT HAS A PYTHON3 COMMAND AVAILABLE!
|
||||
4. Git clone atmosphere (git clone https://github.com/Atmosphere-NX/Atmosphere.git)
|
||||
5. Clone the Horizon OC develop branch (git clone https://github.com/Horizon-OC/Horizon-OC.git --recurse-submodules)
|
||||
5. Clone the Horizon OC develop branch (git clone -b develop --single-branch https://github.com/Horizon-OC/Horizon-OC.git)
|
||||
6. Create a new folder named "build" in the horizon oc repo
|
||||
7. Copy atmosphere files into that build folder
|
||||
8. Copy Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp to build/stratosphere/loader/source/ldr_process_creation.cpp, replacing any files if prompted
|
||||
9. Run ./build.sh in the root directory
|
||||
9. Grab a copy of libultrahand (https://github.com/ppkantorski/libultrahand) and place it into Source/sys-clk/overlay/lib/libultrahand
|
||||
10. Run ./build.sh in the root directory
|
||||
126
README.md
126
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"/>
|
||||
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
---
|
||||
|
||||
@@ -56,7 +56,6 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
|
||||
|
||||
```
|
||||
kip1=atmosphere/kips/hoc.kip
|
||||
secmon=atmosphere/exosphere.bin
|
||||
```
|
||||
|
||||
*(No changes needed if using fusee.)*
|
||||
@@ -79,66 +78,22 @@ Refer to COMPILATION.md
|
||||
---
|
||||
## Clock table
|
||||
|
||||
### MEM clocks (mhz)
|
||||
|
||||
### MEM clocks
|
||||
* 3200 → max on mariko, JEDEC.
|
||||
* 3166
|
||||
* 3133
|
||||
* 3100
|
||||
* 3066
|
||||
* 3033
|
||||
* 3000
|
||||
* 2966
|
||||
* 2933 → JEDEC.
|
||||
* 2900
|
||||
* 2866
|
||||
* 2833
|
||||
* 2800
|
||||
* 2766
|
||||
* 2733
|
||||
* 2700
|
||||
* 2666 → JEDEC.
|
||||
* 2633
|
||||
* 2600
|
||||
* 2566
|
||||
* 2533
|
||||
* 2500
|
||||
* 2466
|
||||
* 2433
|
||||
* 2400 → max on erista, JEDEC.
|
||||
* 2366
|
||||
* 2333
|
||||
* 2300
|
||||
* 2266
|
||||
* 2233
|
||||
* 2200
|
||||
* 2166
|
||||
* 2133 → Mariko JEDEC standard max (4266 Modules)
|
||||
* 2100
|
||||
* 2066
|
||||
* 2033
|
||||
* 2000
|
||||
* 1996 → JEDEC standard
|
||||
* 1966
|
||||
* 1933
|
||||
* 1900
|
||||
* 1866 → Mariko JEDEC standard max (3733 Modules)
|
||||
* 1833
|
||||
* 1800
|
||||
* 1766
|
||||
* 1733
|
||||
* 1700
|
||||
* 1666
|
||||
* 1633
|
||||
* 1600 → official docked, boost mode, Erista JEDEC standard max (3200 Modules), JEDEC.
|
||||
* 2133 → mariko safe max (4266 Modules), JEDEC.
|
||||
* 1996 → JEDEC.
|
||||
* 1866 → mariko safe max (3733 Modules), JEDEC.
|
||||
* 1600 → official docked, boost mode, erista safe max, JEDEC.
|
||||
* 1331 → official handheld, JEDEC.
|
||||
* 1065
|
||||
* 800
|
||||
* 665
|
||||
|
||||
### CPU clocks (mhz)
|
||||
* 2703 → mariko absolute max, dangerous
|
||||
* 2601 → unsafe
|
||||
### CPU clocks
|
||||
* 2601 → mariko absolute max, very dangerous
|
||||
* 2499
|
||||
* 2397 → mariko safe max with UV (low speedo)
|
||||
* 2295
|
||||
@@ -158,18 +113,21 @@ Refer to COMPILATION.md
|
||||
* 816
|
||||
* 714
|
||||
* 612 → sleep mode
|
||||
|
||||
**Notes:**
|
||||
1. On Erista, CPU in handheld is capped to 1581MHz
|
||||
|
||||
### GPU clocks (mhz)
|
||||
### GPU clocks
|
||||
* 1536 → absolute max clock on mariko. very dangerous
|
||||
* 1459
|
||||
* 1382
|
||||
* 1305
|
||||
* 1267 → NVIDIA T214(mariko) rating
|
||||
* 1228 → mariko High UV safe clock
|
||||
* 1152 → mariko hiOpt-15mV max clock
|
||||
* 1075 → mariko hiOpt max clock. absolute max clock on erista. very dangerous
|
||||
* 998 → NVIDIA T210 (erista) rating
|
||||
* 960 (erista only) → erista high uv/hiOpt-15mV safe max clock
|
||||
* 1267 → NVIDIA T214 rating
|
||||
* 1228 → mariko HiOPT safe clock
|
||||
* 1152 → mariko SLT max clock
|
||||
* 1075 → mariko no UV max clock. absolute max clock on erista. very dangerous
|
||||
* 998 → NVIDIA T210 rating
|
||||
* 960 (erista only) → erista slt/hiopt safe max clock
|
||||
* 921 → erista no UV max clock
|
||||
* 844
|
||||
* 768 → official docked
|
||||
@@ -184,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 hiOpt is 614MHz, with hiOpt-15mV it is 691MHz and with High UV it's 768MHz
|
||||
4. Clocks higher than 768MHz on erista need the official charger is plugged in.
|
||||
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
|
||||
* **TDRR** - HOC Logo Design
|
||||
* **tetetete-ctrl** - Website design
|
||||
|
||||
* **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, contributions
|
||||
* **sys-clk team (m4xw, p-sam, natinusala)** - sys-clk
|
||||
* **Dominatorul** - Soctherm driver, guides, general help
|
||||
* **ppkantorski** - 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, Winnerboi77, Blaise, Alvise, agjeococh, frost, letum00, and Xenshen** - Testing
|
||||
* **Samybigio2011, Miki** - Italian translations
|
||||
* **angelblaster** - Korean translations
|
||||
* **q1332348216-glitch** - Chinese translations
|
||||
* **th3-ne0undr5c0r** - French 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,9 +4,7 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.x.x | :white_check_mark: |
|
||||
| 1.x.x | Not supported |
|
||||
| 0.x.x | Not supported |
|
||||
| 0.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-NX
|
||||
* 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,
|
||||
@@ -15,13 +14,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* See https://github.com/lulle2007200/emuMMC/blob/internal-emummc/source/ */
|
||||
#define __ACCESS_TABLE_NAME__ EmcAccessTable1
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceExternalMemoryController1.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_emc_access_table_data.inc"
|
||||
|
||||
#pragma once
|
||||
#include "secmon_define_access_table.inc"
|
||||
|
||||
namespace ams::ldr::hoc {
|
||||
|
||||
void Log(const char *data, ...);
|
||||
void ViewLog();
|
||||
|
||||
}
|
||||
#undef __ACCESS_TABLE_INC__
|
||||
#undef __ACCESS_TABLE_ADDRESS__
|
||||
#undef __ACCESS_TABLE_NAME__
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 CTCaer
|
||||
* 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,
|
||||
@@ -14,11 +14,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SECMON_EXO_H_
|
||||
#define _SECMON_EXO_H_
|
||||
#define __ACCESS_TABLE_NAME__ EmcAccessTable2
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceExternalMemoryController2.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_emc_access_table_data.inc"
|
||||
|
||||
#include <bdk.h>
|
||||
#include "secmon_define_access_table.inc"
|
||||
|
||||
void secmon_exo_check_panic();
|
||||
|
||||
#endif
|
||||
#undef __ACCESS_TABLE_INC__
|
||||
#undef __ACCESS_TABLE_ADDRESS__
|
||||
#undef __ACCESS_TABLE_NAME__
|
||||
@@ -14,9 +14,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __ACCESS_TABLE_NAME__ RtcPmcAccessTable
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceRtcPmc.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_rtc_pmc_access_table_data.inc"
|
||||
#define __ACCESS_TABLE_NAME__ SocthermAccessTable
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceSoctherm.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_soctherm_access_table_data.inc"
|
||||
|
||||
#include "secmon_define_access_table.inc"
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -99,16 +99,20 @@ namespace ams::secmon::smc {
|
||||
#include "secmon_define_pmc_access_table.inc"
|
||||
#include "secmon_define_mc_access_table.inc"
|
||||
#include "secmon_define_emc_access_table.inc"
|
||||
#include "secmon_define_rtc_pmc_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[] = {
|
||||
{ PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, },
|
||||
{ McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, },
|
||||
{ EmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController.GetAddress(), EmcAccessTable::Address, EmcAccessTable::Size, },
|
||||
{ RtcPmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceRtcPmc.GetAddress(), RtcPmcAccessTable::Address, RtcPmcAccessTable::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, },
|
||||
{ PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, },
|
||||
{ McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, },
|
||||
{ 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, },
|
||||
};
|
||||
|
||||
constexpr bool IsAccessAllowed(const AccessTableEntry &entry, uintptr_t address) {
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"name": "Loader",
|
||||
"title_id": "0x0100000000000001",
|
||||
"main_thread_stack_size": "0x4000",
|
||||
"main_thread_priority": 49,
|
||||
"default_cpu_id": 3,
|
||||
"process_category": 1,
|
||||
"use_secure_memory": true,
|
||||
"immortal": true,
|
||||
"kernel_capabilities": [
|
||||
{
|
||||
"type": "handle_table_size",
|
||||
"value": 128
|
||||
},
|
||||
{
|
||||
"type": "syscalls",
|
||||
"value": {
|
||||
"svcSetHeapSize" : "0x01",
|
||||
"svcSetMemoryPermission" : "0x02",
|
||||
"svcSetMemoryAttribute" : "0x03",
|
||||
"svcMapMemory" : "0x04",
|
||||
"svcUnmapMemory" : "0x05",
|
||||
"svcQueryMemory" : "0x06",
|
||||
"svcExitProcess" : "0x07",
|
||||
"svcCreateThread" : "0x08",
|
||||
"svcStartThread" : "0x09",
|
||||
"svcExitThread" : "0x0A",
|
||||
"svcSleepThread" : "0x0B",
|
||||
"svcGetThreadPriority" : "0x0C",
|
||||
"svcSetThreadPriority" : "0x0D",
|
||||
"svcGetThreadCoreMask" : "0x0E",
|
||||
"svcSetThreadCoreMask" : "0x0F",
|
||||
"svcGetCurrentProcessorNumber" : "0x10",
|
||||
"svcSignalEvent" : "0x11",
|
||||
"svcClearEvent" : "0x12",
|
||||
"svcMapSharedMemory" : "0x13",
|
||||
"svcUnmapSharedMemory" : "0x14",
|
||||
"svcCreateTransferMemory" : "0x15",
|
||||
"svcCloseHandle" : "0x16",
|
||||
"svcResetSignal" : "0x17",
|
||||
"svcWaitSynchronization" : "0x18",
|
||||
"svcCancelSynchronization" : "0x19",
|
||||
"svcArbitrateLock" : "0x1A",
|
||||
"svcArbitrateUnlock" : "0x1B",
|
||||
"svcWaitProcessWideKeyAtomic" : "0x1C",
|
||||
"svcSignalProcessWideKey" : "0x1D",
|
||||
"svcGetSystemTick" : "0x1E",
|
||||
"svcConnectToNamedPort" : "0x1F",
|
||||
"svcSendSyncRequestLight" : "0x20",
|
||||
"svcSendSyncRequest" : "0x21",
|
||||
"svcSendSyncRequestWithUserBuffer" : "0x22",
|
||||
"svcSendAsyncRequestWithUserBuffer" : "0x23",
|
||||
"svcGetProcessId" : "0x24",
|
||||
"svcGetThreadId" : "0x25",
|
||||
"svcBreak" : "0x26",
|
||||
"svcOutputDebugString" : "0x27",
|
||||
"svcReturnFromException" : "0x28",
|
||||
"svcGetInfo" : "0x29",
|
||||
"svcWaitForAddress" : "0x34",
|
||||
"svcSignalToAddress" : "0x35",
|
||||
"svcSynchronizePreemptionState" : "0x36",
|
||||
"svcCreateSession" : "0x40",
|
||||
"svcAcceptSession" : "0x41",
|
||||
"svcReplyAndReceiveLight" : "0x42",
|
||||
"svcReplyAndReceive" : "0x43",
|
||||
"svcReplyAndReceiveWithUserBuffer" : "0x44",
|
||||
"svcCreateEvent" : "0x45",
|
||||
"svcReadWriteRegister" : "0x4E",
|
||||
"svcQueryIoMapping" : "0x55",
|
||||
"svcSetProcessMemoryPermission" : "0x73",
|
||||
"svcMapProcessMemory" : "0x74",
|
||||
"svcUnmapProcessMemory" : "0x75",
|
||||
"svcMapProcessCodeMemory" : "0x77",
|
||||
"svcUnmapProcessCodeMemory" : "0x78",
|
||||
"svcCreateProcess" : "0x79",
|
||||
"svcCallSecureMonitor" : "0x7F"
|
||||
}
|
||||
}, {
|
||||
"type": "map",
|
||||
"value": {
|
||||
"address": "0x7000F000",
|
||||
"is_ro": false,
|
||||
"size": "0x00001000",
|
||||
"is_io": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
51
Source/Atmosphere/stratosphere/loader/source/oc/Makefile
Normal file
51
Source/Atmosphere/stratosphere/loader/source/oc/Makefile
Normal file
@@ -0,0 +1,51 @@
|
||||
TARGET_EXEC := test
|
||||
|
||||
BUILD_DIR := ./build
|
||||
SRC_DIRS := ./
|
||||
|
||||
# CXX := clang++ g++-12
|
||||
|
||||
# Find all the C and C++ files we want to compile
|
||||
# Note the single quotes around the * expressions. Make will incorrectly expand these otherwise.
|
||||
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')
|
||||
|
||||
# String substitution for every C/C++ file.
|
||||
# As an example, hello.cpp turns into ./build/hello.cpp.o
|
||||
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
|
||||
|
||||
# String substitution (suffix version without %).
|
||||
# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d
|
||||
DEPS := $(OBJS:.o=.d)
|
||||
|
||||
# Every folder in ./src will need to be passed to GCC so that it can find header files
|
||||
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
|
||||
# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag
|
||||
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
|
||||
|
||||
CPPFLAGS := $(INC_FLAGS) -Wall -Werror -Wno-unused-result -std=c++20 -Og -g
|
||||
|
||||
# The final build step.
|
||||
$(TARGET_EXEC): $(OBJS)
|
||||
@echo "Linking $@"
|
||||
@$(CXX) $(OBJS) -o $@ $(LDFLAGS)
|
||||
|
||||
# Build step for C source
|
||||
$(BUILD_DIR)/%.c.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
@echo "$<"
|
||||
@$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Build step for C++ source
|
||||
$(BUILD_DIR)/%.cpp.o: %.cpp
|
||||
@mkdir -p $(dir $@)
|
||||
@echo "$<"
|
||||
@$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -r $(BUILD_DIR) $(TARGET_EXEC)
|
||||
|
||||
# Include the .d makefiles. The - at the front suppresses the errors of missing
|
||||
# Makefiles. Initially, all the .d files will be missing, and we don't want those
|
||||
# errors to show up.
|
||||
-include $(DEPS)
|
||||
@@ -26,117 +26,88 @@
|
||||
#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 {
|
||||
|
||||
volatile CustomizeTable C = {
|
||||
|
||||
/* Disables RAM powerdown */
|
||||
.hpMode = DISABLED,
|
||||
|
||||
.commonEmcMemVolt = 1175000, /* LPDDR4(X) JEDEC Specification */
|
||||
.eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */
|
||||
.commonEmcMemVolt = 1175000, /* LPDDR4X JEDEC Specification */
|
||||
.eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */
|
||||
.eristaEmcMaxClock1 = 1600000,
|
||||
.eristaEmcMaxClock2 = 1600000,
|
||||
|
||||
/* Available: 66MHz step rate, 100MHz step rate, 133MHz step rate and jedec. */
|
||||
/* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */
|
||||
.stepMode = StepMode_66MHz,
|
||||
.marikoEmcMaxClock = 1866000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
|
||||
.marikoEmcVddqVolt = 600000, /* Micron: 600mV, other manafacturers: 640mV */
|
||||
.emcDvbShift = 0,
|
||||
|
||||
.marikoEmcMaxClock = 2133000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
|
||||
.marikoEmcVddqVolt = 600000,
|
||||
|
||||
.emcDvbShift = 0,
|
||||
.marikoSocVmax = 0, /* 0 = stock limits (1450 - 1597 is 1050mV, 1598-1708 is 1025mV, 1709+ is 1000mV). */
|
||||
|
||||
/* Primary. */
|
||||
.t1_tRCD = 0,
|
||||
.t2_tRP = 0,
|
||||
.t3_tRAS = 0,
|
||||
/* Secondary. */
|
||||
.t4_tRRD = 0,
|
||||
.t5_tRFC = 0,
|
||||
.t6_tRTW = 0,
|
||||
.t7_tWTR = 0,
|
||||
.t8_tREFI = 0,
|
||||
|
||||
/* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
|
||||
/* This is a lazy workaround until I find the issue... */
|
||||
.t2_tRP_cap = 2,
|
||||
|
||||
/* Frequency where non low timings gets used. */
|
||||
.timingEmcTbreak = DISABLED,
|
||||
.low_t6_tRTW = 0,
|
||||
.low_t7_tWTR = 0,
|
||||
|
||||
.readLatency = {
|
||||
/* 1333 */ 0,
|
||||
/* 1600 */ 0,
|
||||
/* 1866 */ 0,
|
||||
/* 2133 */ 0,
|
||||
},
|
||||
|
||||
.writeLatency = {
|
||||
/* 1333 */ 0,
|
||||
/* 1600 */ 0,
|
||||
/* 1866 */ 0,
|
||||
/* 2133 */ 0,
|
||||
},
|
||||
// Primary
|
||||
.t1_tRCD = 0,
|
||||
.t2_tRP = 0,
|
||||
.t3_tRAS = 0,
|
||||
// Secondary
|
||||
.t4_tRRD = 0,
|
||||
.t5_tRFC = 0,
|
||||
.t6_tRTW = 0,
|
||||
.t7_tWTR = 0,
|
||||
.t8_tREFI = 0,
|
||||
|
||||
/* You can mix and match different latencies if needed */
|
||||
/*
|
||||
* Read:
|
||||
* 2133RL = 40
|
||||
* 1866RL = 36
|
||||
* 1600RL = 32
|
||||
* 1331RL = 28
|
||||
* Write:
|
||||
* 2133WL = 18
|
||||
* 1866WL = 16
|
||||
* 1600WL = 14
|
||||
* 1331WL = 12
|
||||
* Read:
|
||||
* 2133RL = 40
|
||||
* 1866RL = 36
|
||||
* 1600RL = 32
|
||||
* 1331RL = 28
|
||||
* Write:
|
||||
* 2133WL = 18
|
||||
* 1866WL = 16
|
||||
* 1600WL = 14
|
||||
* 1331WL = 12
|
||||
*/
|
||||
|
||||
/* Erista only. */
|
||||
.mem_burst_read_latency = RL_1600,
|
||||
.mem_burst_write_latency = WL_1600,
|
||||
.mem_burst_read_latency = RL_1866,
|
||||
.mem_burst_write_latency = WL_1866,
|
||||
|
||||
.eristaCpuUV = 0,
|
||||
.eristaCpuVmin = 800,
|
||||
.eristaCpuUV = 0,
|
||||
.eristaCpuVmin = 800,
|
||||
.eristaCpuMaxVolt = 1200,
|
||||
/* Unlocks up to 2397 Mhz CPU, usage is not recommended. */
|
||||
.eristaCpuUnlock = DISABLED,
|
||||
/* Unlocks up to 2295 Mhz CPU, usage is not recommended. */
|
||||
.eristaCpuUnlock = DISABLED,
|
||||
|
||||
.marikoCpuUVLow = 0, // No undervolt
|
||||
.marikoCpuUVLow = 0, // No undervolt
|
||||
.marikoCpuUVHigh = 0, // No undervolt
|
||||
|
||||
.tableConf = TBREAK_1683,
|
||||
.marikoCpuLowVmin = 620,
|
||||
.tableConf = DEFAULT_TABLE,
|
||||
.marikoCpuLowVmin = 620,
|
||||
.marikoCpuHighVmin = 750,
|
||||
|
||||
/* 1120mV is NVIDIA rating */
|
||||
.marikoCpuMaxVolt = 1120,
|
||||
.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 */
|
||||
.eristaCpuBoostClock = 1785000, // Default boost clock
|
||||
.marikoCpuBoostClock = 1963000, // Default boost clock
|
||||
|
||||
.eristaGpuUV = 0,
|
||||
.eristaGpuUV = 0,
|
||||
.eristaGpuVmin = 810,
|
||||
|
||||
.marikoGpuUV = 0,
|
||||
/* Vmin past 795mV won't work due boot voltage being 800mV (can be adjusted though). */
|
||||
.marikoGpuVmin = 610,
|
||||
.marikoGpuBootVolt = 800, /* Used during boot and when temp is <20°C */
|
||||
|
||||
/* 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,
|
||||
|
||||
.commonGpuVoltOffset = 0,
|
||||
@@ -148,34 +119,31 @@ volatile CustomizeTable C = {
|
||||
/* AUTO: Voltage is optimally chosen; with commonGpuVoltOffset applied. */
|
||||
/* AUTO only works up to 1305 GPU on Mariko and 998 GPU on Erista (it is reccomended to manually set your 998MHz voltage though) */
|
||||
/* You can overwrite auto with any voltage (in mv) of your choice - offset will not be applied. */
|
||||
|
||||
.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 = {
|
||||
@@ -195,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) */,
|
||||
@@ -203,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. */
|
||||
@@ -227,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 = {
|
||||
@@ -253,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 = {
|
||||
@@ -276,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, }, { } },
|
||||
@@ -308,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, }, { } },
|
||||
@@ -336,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, }, { } },
|
||||
@@ -364,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, }, { } },
|
||||
@@ -392,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 = {
|
||||
@@ -494,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 = {
|
||||
@@ -516,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 = {
|
||||
@@ -544,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 } },
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -20,27 +20,34 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CUST_REV 4
|
||||
#define KIP_VERSION 240
|
||||
#define CUST_REV 1
|
||||
|
||||
#include "oc_common.hpp"
|
||||
#include "pcv/pcv_common.hpp"
|
||||
|
||||
namespace ams::ldr::hoc {
|
||||
|
||||
#include "mtc_timing_table.hpp"
|
||||
|
||||
enum TableConfig: u32 {
|
||||
DEFAULT_TABLE = 1,
|
||||
TBREAK_1581 = 2,
|
||||
TBREAK_1683 = 3,
|
||||
TBREAK_1581 = 2,
|
||||
TBREAK_1683 = 3,
|
||||
EXTREME_TABLE = 4,
|
||||
};
|
||||
|
||||
enum StepMode: u32 {
|
||||
StepMode_66MHz = 0,
|
||||
StepMode_100MHz = 1,
|
||||
StepMode_Jedec = 2,
|
||||
StepMode_133MHz = 3,
|
||||
};
|
||||
/*
|
||||
* Read:
|
||||
* 2133RL = 40
|
||||
* 1866RL = 36
|
||||
* 1600RL = 32
|
||||
* 1331RL = 28
|
||||
* Write:
|
||||
* 2133WL = 18
|
||||
* 1866WL = 16
|
||||
* 1600WL = 14
|
||||
* 1331WL = 12
|
||||
*/
|
||||
|
||||
enum ReadLatency: u32 {
|
||||
RL_2133 = 40,
|
||||
@@ -61,23 +68,23 @@ using CustomizeGpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit];
|
||||
static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(CustomizeGpuDvfsTable));
|
||||
static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(pcv::cvb_entry_t) * pcv::DvfsTableEntryLimit);
|
||||
|
||||
struct CustomizeTable {
|
||||
u8 cust[4] = {'C', 'U', 'S', 'T'};
|
||||
u32 custRev = CUST_REV;
|
||||
u32 kipVersion = KIP_VERSION;
|
||||
constexpr uint32_t ERISTA_MTC_MAGIC = 0x43544D45; // EMTC
|
||||
constexpr uint32_t MARIKO_MTC_MAGIC = 0x43544D4D; // MMTC
|
||||
|
||||
typedef struct CustomizeTable {
|
||||
u8 cust[4] = {'C', 'U', 'S', 'T'};
|
||||
u32 custRev = CUST_REV;
|
||||
|
||||
u32 placeholder;
|
||||
u32 hpMode;
|
||||
|
||||
u32 commonEmcMemVolt;
|
||||
u32 eristaEmcMaxClock;
|
||||
u32 eristaEmcMaxClock1;
|
||||
u32 eristaEmcMaxClock2;
|
||||
|
||||
StepMode stepMode;
|
||||
u32 marikoEmcMaxClock;
|
||||
u32 marikoEmcVddqVolt;
|
||||
s32 emcDvbShift;
|
||||
u32 marikoSocVmax;
|
||||
u32 emcDvbShift;
|
||||
// advanced config
|
||||
u32 t1_tRCD;
|
||||
u32 t2_tRP;
|
||||
@@ -88,15 +95,6 @@ struct CustomizeTable {
|
||||
u32 t7_tWTR;
|
||||
u32 t8_tREFI;
|
||||
|
||||
u32 t2_tRP_cap;
|
||||
|
||||
u32 timingEmcTbreak;
|
||||
u32 low_t6_tRTW;
|
||||
u32 low_t7_tWTR;
|
||||
|
||||
u32 readLatency[4];
|
||||
u32 writeLatency[4];
|
||||
|
||||
u32 mem_burst_read_latency;
|
||||
u32 mem_burst_write_latency;
|
||||
|
||||
@@ -121,15 +119,14 @@ struct CustomizeTable {
|
||||
|
||||
u32 marikoGpuUV;
|
||||
u32 marikoGpuVmin;
|
||||
u32 marikoGpuBootVolt;
|
||||
u32 marikoGpuVmax;
|
||||
|
||||
u32 commonGpuVoltOffset;
|
||||
|
||||
u32 gpuSpeedo;
|
||||
|
||||
u32 eristaGpuVoltArray[27];
|
||||
u32 marikoGpuVoltArray[24];
|
||||
u32 eristaGpuVoltArray[24];
|
||||
u32 marikoGpuVoltArray[31];
|
||||
|
||||
u32 fineTune_t6_tRTW;
|
||||
u32 fineTune_t7_tWTR;
|
||||
@@ -153,7 +150,7 @@ struct CustomizeTable {
|
||||
CustomizeGpuDvfsTable marikoGpuDvfsTableSLT;
|
||||
CustomizeGpuDvfsTable marikoGpuDvfsTableHiOPT;
|
||||
|
||||
};
|
||||
} CustomizeTable;
|
||||
|
||||
extern volatile CustomizeTable C;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../oc_common.hpp"
|
||||
#include "../mtc_timing_value.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stratosphere.hpp>
|
||||
#include "../oc_common.hpp"
|
||||
#include "../mtc_timing_value.hpp"
|
||||
#include "timing_tables.hpp"
|
||||
|
||||
@@ -26,72 +26,8 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
return;
|
||||
}
|
||||
|
||||
/* > 3200 */
|
||||
rext = 0x1E;
|
||||
}
|
||||
|
||||
void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) {
|
||||
latency += index * latencyStep;
|
||||
}
|
||||
|
||||
static s32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) {
|
||||
s32 maxIndex = -1;
|
||||
for (u32 i = 0; i < latencySize; ++i) {
|
||||
if (latencyArray[i]) {
|
||||
maxIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
return maxIndex;
|
||||
}
|
||||
|
||||
void AutoLatency(volatile u32 &latency, u32 freq, u32 latencyStep) {
|
||||
if (freq > 1600'000 && freq <= 1866'000) { /* 1866tRWL */
|
||||
latency += latencyStep * 2;
|
||||
} else { /* 2133tRWL */
|
||||
latency += latencyStep * 3;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleLatency(u32 freq, volatile u32 &latency, volatile u32 *latencyArray, u32 indexMax, u32 latencyStep) {
|
||||
for (u32 i = 0; i <= indexMax; ++i) {
|
||||
if (latencyArray[i] != 0 && freq <= latencyArray[i]) {
|
||||
SwitchLatency(latency, i, latencyStep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SwitchLatency(latency, indexMax, latencyStep);
|
||||
}
|
||||
|
||||
void HandleLatency(u32 freq) {
|
||||
static s32 rlIndexMax = GetMaxLatencyIndex(C.readLatency, std::size(C.readLatency));
|
||||
static s32 wlIndexMax = GetMaxLatencyIndex(C.writeLatency, std::size(C.writeLatency));
|
||||
constexpr u32 ReadLatencyStep = 4;
|
||||
constexpr u32 WriteLatencyStep = 2;
|
||||
bool autoLatencyRead = false, autoLatencyWrite = false;
|
||||
|
||||
if (rlIndexMax == -1) {
|
||||
AutoLatency(RL, freq, ReadLatencyStep);
|
||||
autoLatencyRead = true;
|
||||
}
|
||||
|
||||
if (wlIndexMax == -1) {
|
||||
AutoLatency(WL, freq, WriteLatencyStep);
|
||||
autoLatencyWrite = true;
|
||||
}
|
||||
|
||||
if (autoLatencyRead && autoLatencyWrite) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!autoLatencyRead) {
|
||||
HandleLatency(freq, RL, C.readLatency, rlIndexMax, ReadLatencyStep);
|
||||
}
|
||||
|
||||
if (!autoLatencyWrite) {
|
||||
HandleLatency(freq, WL, C.writeLatency, wlIndexMax, WriteLatencyStep);
|
||||
}
|
||||
/* Fallback. */
|
||||
rext = 0x1A;
|
||||
}
|
||||
|
||||
void CalculateMrw2() {
|
||||
@@ -120,84 +56,11 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
}
|
||||
}
|
||||
|
||||
/* DBI is always enabled. */
|
||||
mrw2 = static_cast<u8>(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6)));
|
||||
}
|
||||
|
||||
void CalculateTimings(double tCK_avg, u32 freq) {
|
||||
RL = RL_1331;
|
||||
WL = WL_1331;
|
||||
|
||||
HandleLatency(freq);
|
||||
|
||||
void CalculateTimings() {
|
||||
GetRext();
|
||||
|
||||
/* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
|
||||
/* This is a lazy workaround until I find the issue... */
|
||||
u32 tRPpbIndex = C.t2_tRP;
|
||||
if (WL == WL_1331) {
|
||||
tRPpbIndex = MIN(C.t2_tRP_cap, C.t2_tRP);
|
||||
}
|
||||
|
||||
tRCD = tRCD_values[C.t1_tRCD];
|
||||
tRPpb = tRP_values[tRPpbIndex];
|
||||
tRAS = tRAS_values[C.t3_tRAS];
|
||||
tRRD = tRRD_values[C.t4_tRRD];
|
||||
tRFCpb = tRFC_values[C.t5_tRFC];
|
||||
u32 tRTW = C.t6_tRTW;
|
||||
u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
|
||||
|
||||
if (freq < C.timingEmcTbreak) {
|
||||
tRTW = C.low_t6_tRTW;
|
||||
tWTR = 10 - tWTR_values[C.low_t7_tWTR];
|
||||
}
|
||||
|
||||
s32 finetRTW = C.fineTune_t6_tRTW;
|
||||
s32 finetWTR = C.fineTune_t7_tWTR;
|
||||
|
||||
tRC = tRAS + tRPpb;
|
||||
tRFCab = tRFCpb * 2;
|
||||
tXSR = static_cast<double>(tRFCab + 7.5);
|
||||
tFAW = static_cast<u32>(tRRD * 4.0);
|
||||
tRPab = tRPpb + 3;
|
||||
|
||||
tR2P = CEIL((RL * 0.426) - 2.0);
|
||||
tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (tRTW * 3) + finetRTW;
|
||||
tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
|
||||
tRATM = CEIL((tRTM - 10.0) + (RL * 0.426));
|
||||
|
||||
rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017);
|
||||
qpop = rdv - 14;
|
||||
quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782);
|
||||
quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg);
|
||||
einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width;
|
||||
einput = quse - CEIL(9.928 / tCK_avg);
|
||||
u32 qrst_duration = FLOOR(8.399 - tCK_avg);
|
||||
u32 qrstLow = MAX(static_cast<s32>(einput - qrst_duration - 2), static_cast<s32>(0));
|
||||
qrst = PACK_U32(qrst_duration, qrstLow);
|
||||
ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0);
|
||||
qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput);
|
||||
tW2P = (CEIL(WL * 1.7303) * 2) - 5;
|
||||
tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast<double>(tW2P))) + (BL / 2));
|
||||
tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR;
|
||||
tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753));
|
||||
tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0;
|
||||
|
||||
wdv = WL;
|
||||
wsv = WL - 2;
|
||||
wev = 0xA + (WL - 14);
|
||||
|
||||
u32 obdlyHigh = 3 / FLOOR(MIN(static_cast<double>(2), tCK_avg * (WL - 7)));
|
||||
u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0);
|
||||
obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow);
|
||||
|
||||
pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361)));
|
||||
|
||||
tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0;
|
||||
|
||||
u32 tMMRI = tRCD + (tCK_avg * 3);
|
||||
pdex2mrr = tMMRI + 10;
|
||||
|
||||
CalculateMrw2();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
void CalculateTimings(double tCK_avg, u32 freq);
|
||||
void CalculateTimings();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../oc_common.hpp"
|
||||
#include "../mtc_timing_value.hpp"
|
||||
#include "timing_tables.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "../mtc_timing_value.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
|
||||
@@ -111,54 +111,60 @@ namespace ams::ldr::hoc {
|
||||
const std::array<u32, 10> tWTR_values = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
|
||||
const std::array<u32, 6> tREFpb_values = { 3900, 5850, 7800, 11700, 15600, 99999 };
|
||||
|
||||
inline u32 tRCD;
|
||||
inline u32 tRPpb;
|
||||
inline u32 tRAS;
|
||||
inline double tRRD;
|
||||
inline u32 tRFCpb;
|
||||
const double tCK_avg = 1000'000.0 / C.marikoEmcMaxClock;
|
||||
|
||||
inline u32 tRC;
|
||||
inline u32 tRFCab;
|
||||
inline double tXSR;
|
||||
inline u32 tFAW;
|
||||
inline double tRPab;
|
||||
const u32 tRCD = tRCD_values[C.t1_tRCD];
|
||||
const u32 tRPpb = tRP_values[C.t2_tRP];
|
||||
const u32 tRAS = tRAS_values[C.t3_tRAS];
|
||||
const double tRRD = tRRD_values[C.t4_tRRD];
|
||||
const u32 tRFCpb = tRFC_values[C.t5_tRFC];
|
||||
const u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
|
||||
const s32 finetRTW = C.fineTune_t6_tRTW;
|
||||
const s32 finetWTR = C.fineTune_t7_tWTR;
|
||||
|
||||
inline u32 RL;
|
||||
inline u32 WL;
|
||||
const u32 tRC = tRAS + tRPpb;
|
||||
const u32 tRFCab = tRFCpb * 2;
|
||||
const double tXSR = static_cast<double>(tRFCab + 7.5);
|
||||
const u32 tFAW = static_cast<u32>(tRRD * 4.0);
|
||||
const double tRPab = tRPpb + 3;
|
||||
|
||||
inline u32 tR2P;
|
||||
inline u32 tR2W;
|
||||
inline u32 tRTM;
|
||||
inline u32 tRATM;
|
||||
const u32 tR2P = CEIL((RL * 0.426) - 2.0);
|
||||
const u32 tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW;
|
||||
const u32 tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
|
||||
const u32 tRATM = CEIL((tRTM - 10.0) + (RL * 0.426));
|
||||
inline u32 rext;
|
||||
|
||||
inline u32 rdv;
|
||||
inline u32 qpop;
|
||||
inline u32 quse_width;
|
||||
inline u32 quse;
|
||||
inline u32 einput_duration;
|
||||
inline u32 einput;
|
||||
inline u32 qrst;
|
||||
inline u32 ibdly;
|
||||
inline u32 qsafe;
|
||||
const u32 rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017);
|
||||
const u32 qpop = rdv - 14;
|
||||
const u32 quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782);
|
||||
const u32 quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg);
|
||||
const u32 einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width;
|
||||
const u32 einput = quse - CEIL(9.928 / tCK_avg);
|
||||
const u32 qrst_duration = FLOOR(8.399 - tCK_avg);
|
||||
const u32 qrstLow = MAX(static_cast<s32>(einput - qrst_duration - 2), static_cast<s32>(0));
|
||||
const u32 qrst = PACK_U32(qrst_duration, qrstLow);
|
||||
const u32 ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0);
|
||||
const u32 qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput);
|
||||
const u32 tW2P = (CEIL(WL * 1.7303) * 2) - 5;
|
||||
const u32 tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast<double>(tW2P))) + (BL / 2));
|
||||
const u32 tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR;
|
||||
const u32 tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753));
|
||||
const u32 tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0;
|
||||
|
||||
inline u32 tW2P;
|
||||
inline u32 tWTPDEN;
|
||||
inline u32 tW2R;
|
||||
inline u32 tWTM;
|
||||
inline u32 tWATM;
|
||||
const u32 wdv = WL;
|
||||
const u32 wsv = WL - 2;
|
||||
const u32 wev = 0xA + (WL - 14);
|
||||
|
||||
inline u32 wdv;
|
||||
inline u32 wsv;
|
||||
inline u32 wev;
|
||||
const u32 obdlyHigh = 3 / FLOOR(MIN(static_cast<double>(2), tCK_avg * (WL - 7)));
|
||||
const u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0);
|
||||
const u32 obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow);
|
||||
|
||||
inline u32 obdly;
|
||||
const u32 pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361)));
|
||||
|
||||
inline u32 pdex2rw;
|
||||
const u32 tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0;
|
||||
|
||||
inline u32 tCLKSTOP;
|
||||
|
||||
inline u32 pdex2mrr;
|
||||
const double tMMRI = tRCD + (tCK_avg * 3);
|
||||
const double pdex2mrr = tMMRI + 10; /* Do this properly? */
|
||||
|
||||
inline u8 mrw2;
|
||||
}
|
||||
|
||||
@@ -18,17 +18,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stratosphere.hpp>
|
||||
#include <vapours/results/results_common.hpp>
|
||||
#define LOGGING(fmt, ...) ((void)0)
|
||||
#define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); }
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <stratosphere.hpp>
|
||||
#include <vapours/results/results_common.hpp>
|
||||
#define LOGGING(fmt, ...) ((void)0)
|
||||
#define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); }
|
||||
#else
|
||||
#include "oc_test.hpp"
|
||||
#endif
|
||||
|
||||
#include "customize.hpp"
|
||||
|
||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||
#include "oc_log.hpp"
|
||||
#endif
|
||||
|
||||
#define PATCH_OFFSET(offset, value) \
|
||||
static_assert(sizeof(__typeof__(offset)) <= sizeof(u64)); \
|
||||
*(offset) = value;
|
||||
@@ -50,9 +50,6 @@ namespace ams::ldr {
|
||||
R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013);
|
||||
R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014);
|
||||
R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015);
|
||||
R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
|
||||
R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017);
|
||||
R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018);
|
||||
}
|
||||
|
||||
namespace ams::ldr::hoc {
|
||||
@@ -66,65 +63,40 @@ namespace ams::ldr::hoc {
|
||||
size_t maximum_patched_count = 0;
|
||||
patternFn pattern_search_fn = nullptr;
|
||||
Pointer value_search;
|
||||
|
||||
size_t patched_count = 0;
|
||||
|
||||
Result Apply(Pointer *ptr) {
|
||||
Result Apply(Pointer* ptr) {
|
||||
Result res = patcher_fn(ptr);
|
||||
if (R_SUCCEEDED(res)) {
|
||||
if (R_SUCCEEDED(res))
|
||||
patched_count++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Result SearchAndApply(Pointer *ptr) {
|
||||
Result SearchAndApply(Pointer* ptr) {
|
||||
bool searchOk = false;
|
||||
if (pattern_search_fn) {
|
||||
if (pattern_search_fn(ptr)) {
|
||||
searchOk = true;
|
||||
}
|
||||
if (pattern_search_fn(ptr)) searchOk = true;
|
||||
} else {
|
||||
if (value_search == *(ptr)) {
|
||||
searchOk = true;
|
||||
}
|
||||
if (value_search == *(ptr)) searchOk = true;
|
||||
}
|
||||
|
||||
if (searchOk) {
|
||||
if (searchOk)
|
||||
return Apply(ptr);
|
||||
}
|
||||
|
||||
R_THROW(ldr::ResultUnsuccessfulPatcher());
|
||||
}
|
||||
|
||||
Result CheckResult() {
|
||||
#ifndef ATMOSPHERE_IS_STRATOSPHERE
|
||||
R_UNLESS(patched_count > 0, ldr::ResultUnsuccessfulPatcher());
|
||||
#endif
|
||||
|
||||
if (maximum_patched_count) {
|
||||
if (maximum_patched_count)
|
||||
R_UNLESS(patched_count <= maximum_patched_count, ldr::ResultUnsuccessfulPatcher());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
|
||||
* Copyright (c) 2019 Atmosphere-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/>.
|
||||
*/
|
||||
|
||||
/* See https://github.com/lulle2007200/emuMMC/blob/internal-emummc/source/ */
|
||||
|
||||
#include "oc_common.hpp"
|
||||
|
||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||
#include "fatal_handler_bin.h"
|
||||
#endif
|
||||
|
||||
namespace ams::ldr::hoc {
|
||||
|
||||
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x32454641
|
||||
#define ATMOSPHERE_IRAM_PAYLOAD_BASE 0x40010000
|
||||
#define ATMOSPHERE_FATAL_ERROR_ADDR 0x4003E000
|
||||
|
||||
_Alignas(4096) u8 working_buf[4096];
|
||||
|
||||
void SmcRebootToIramPayload() {
|
||||
SecmonArgs args;
|
||||
args.X[0] = 0xC3000401;
|
||||
args.X[1] = 65001;
|
||||
args.X[2] = 0;
|
||||
args.X[3] = 2;
|
||||
svcCallSecureMonitor(&args);
|
||||
}
|
||||
|
||||
Result SmcCopyToIram(uintptr_t dest, const void *src, u32 size) {
|
||||
SecmonArgs args;
|
||||
args.X[0] = 0xF0000201;
|
||||
args.X[1] = (u64)src;
|
||||
args.X[2] = (u64)dest;
|
||||
args.X[3] = size;
|
||||
args.X[4] = 1;
|
||||
svcCallSecureMonitor(&args);
|
||||
Result rc = 0;
|
||||
if (args.X[0] != 0) {
|
||||
rc = (26u | ((u32)args.X[0] << 9u));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result SmcCopyFromIram(void *dest, uintptr_t src, u32 size) {
|
||||
SecmonArgs args;
|
||||
args.X[0] = 0xF0000201;
|
||||
args.X[1] = (u64)dest;
|
||||
args.X[2] = (u64)src;
|
||||
args.X[3] = size;
|
||||
args.X[4] = 0;
|
||||
svcCallSecureMonitor(&args);
|
||||
Result rc = 0;
|
||||
if (args.X[0] != 0) {
|
||||
rc = (26u | ((u32)args.X[0] << 9u));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct log_ctx_t {
|
||||
u32 magic;
|
||||
u32 sz;
|
||||
u32 start;
|
||||
u32 end;
|
||||
char buf[];
|
||||
};
|
||||
|
||||
#define IRAM_LOG_CTX_ADDR 0x4003C000
|
||||
#define IRAM_LOG_MAX_SZ 4096
|
||||
|
||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||
void Log(const char *data, ...) {
|
||||
static const u32 max_log_sz = sizeof(working_buf) - sizeof(log_ctx_t);
|
||||
static bool initDone = false;
|
||||
log_ctx_t *log_ctx = (log_ctx_t*)working_buf;
|
||||
|
||||
R_DISCARD(SmcCopyFromIram(working_buf, IRAM_LOG_CTX_ADDR, sizeof(working_buf)));
|
||||
|
||||
if (!initDone) {
|
||||
initDone = true;
|
||||
log_ctx->buf[0] = '\0';
|
||||
log_ctx->magic = 0xaabbccdd;
|
||||
log_ctx->start = 0;
|
||||
log_ctx->end = 0;
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, data);
|
||||
s32 res = vsnprintf(log_ctx->buf + log_ctx->end, max_log_sz - log_ctx->end, data, args);
|
||||
va_end(args);
|
||||
|
||||
if (res < 0 || res >= (static_cast<s32>(max_log_sz - log_ctx->end))) {
|
||||
R_DISCARD(SmcCopyToIram(IRAM_LOG_CTX_ADDR, working_buf, sizeof(working_buf)));
|
||||
return;
|
||||
}
|
||||
|
||||
log_ctx->end += res;
|
||||
R_DISCARD(SmcCopyToIram(IRAM_LOG_CTX_ADDR, working_buf, sizeof(working_buf)));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||
void ViewLog() {
|
||||
if (spl::GetSocType() == spl::SocType_Mariko) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr size_t PageSize = 4096;
|
||||
for (size_t ofs = 0; ofs < fatal_handler_bin_size; ofs += PageSize) {
|
||||
memcpy(&working_buf, fatal_handler_bin + ofs, std::min(fatal_handler_bin_size - ofs, PageSize));
|
||||
R_DISCARD(SmcCopyToIram(ATMOSPHERE_IRAM_PAYLOAD_BASE + ofs, &working_buf, std::min(fatal_handler_bin_size - ofs, PageSize)));
|
||||
}
|
||||
|
||||
SmcRebootToIramPayload();
|
||||
|
||||
while(true){}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
214
Source/Atmosphere/stratosphere/loader/source/oc/oc_test.cpp
Normal file
214
Source/Atmosphere/stratosphere/loader/source/oc/oc_test.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) Switch-OC-Suite
|
||||
*
|
||||
* 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 ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include "oc_test.hpp"
|
||||
#include "oc_loader.hpp"
|
||||
|
||||
void* loadExec(const char* file_loc, size_t* out_size) {
|
||||
FILE* fp = fopen(file_loc, "rb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Cannot open file: \"%s\"\n", file_loc);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_END) < 0) {
|
||||
fprintf(stderr, "fseek error\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
long size = ftell(fp);
|
||||
if (size == -1L) {
|
||||
fprintf(stderr, "\"%s\" is a directory", file_loc);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
void* buf = malloc(size);
|
||||
|
||||
fread(buf, size, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
if (size < 8192) {
|
||||
fprintf(stderr, "File is too small to process: \"%s\" (%ld B)\n", file_loc, size);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
*out_size = size;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void saveExec(const char* file_loc, const void* buf, size_t size) {
|
||||
FILE* fp = fopen(file_loc, "wb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Cannot write to \"%s\"\n", file_loc);
|
||||
exit(-1);
|
||||
}
|
||||
printf("Saving to \"%s\"...\n", file_loc);
|
||||
fwrite(buf, size, 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
Result Test_PcvDvfsTable() {
|
||||
using namespace ams::ldr::hoc::pcv;
|
||||
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&mariko::CpuCvbTableDefault)) == 18);
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&erista::CpuCvbTableDefault)) == 16);
|
||||
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&mariko::GpuCvbTableDefault)) == 17);
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&erista::GpuCvbTableDefault)) == 12);
|
||||
|
||||
cvb_entry_t last_mariko_cpu_cvb_entry_default = { 1963500, { 1675751, -38635, 27 }, { 1120000 } };
|
||||
assert(memcmp(GetDvfsTableLastEntry((cvb_entry_t *)(&mariko::CpuCvbTableDefault)), (void *)&last_mariko_cpu_cvb_entry_default, sizeof(last_mariko_cpu_cvb_entry_default)) == 0);
|
||||
assert(GetDvfsTableLastEntry((cvb_entry_t *)(&erista::GpuCvbTableDefault))->freq == 921600);
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::hoc::C.marikoCpuDvfsTableSLT)) == 25);
|
||||
|
||||
// Customized table default
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::hoc::C.eristaCpuDvfsTable)) == 19);
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::hoc::C.marikoCpuDvfsTable)) == 21);
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::hoc::C.marikoCpuDvfsTableSLT)) == 22);
|
||||
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::hoc::C.eristaGpuDvfsTable)) == 12);
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::hoc::C.marikoGpuDvfsTable)) == 17);
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::hoc::C.marikoGpuDvfsTableSLT)) == 17);
|
||||
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::hoc::C.marikoGpuDvfsTableHiOPT)) == 17);
|
||||
|
||||
constexpr size_t limit = ams::ldr::hoc::pcv::DvfsTableEntryLimit;
|
||||
cvb_entry_t customized_table[limit] = {};
|
||||
for (size_t i = 0; i < limit; i++) {
|
||||
assert(GetDvfsTableEntryCount(customized_table) == i);
|
||||
auto p = GetDvfsTableLastEntry(customized_table);
|
||||
if (p)
|
||||
assert(p->freq == i);
|
||||
|
||||
customized_table[i].freq = i + 1;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void unitTest() {
|
||||
UnitTest test[] = {
|
||||
{ "PCV DVFS Table", &Test_PcvDvfsTable }
|
||||
};
|
||||
|
||||
for (auto &t : test) {
|
||||
t.Test();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
unitTest();
|
||||
|
||||
const char* pcv_opt = "pcv";
|
||||
const char* ptm_opt = "ptm";
|
||||
const char* save_opt = "-s";
|
||||
const char* mariko_ext = ".mariko";
|
||||
const char* erista_ext = ".erista";
|
||||
enum EXE_OPTION {
|
||||
EXE_PCV,
|
||||
EXE_PTM,
|
||||
UNKNOWN
|
||||
};
|
||||
EXE_OPTION exe_opt = UNKNOWN;
|
||||
if (argc > 2) {
|
||||
if (!strcmp(argv[1], pcv_opt))
|
||||
exe_opt = EXE_PCV;
|
||||
|
||||
if (!strcmp(argv[1], ptm_opt))
|
||||
exe_opt = EXE_PTM;
|
||||
}
|
||||
if ((argc != 3 && argc != 4) || exe_opt == UNKNOWN) {
|
||||
fprintf(stderr, "Usage:\n"\
|
||||
" %s %s | %s [%s] <exec_path>\n\n"\
|
||||
" %s : Save patched executable with extension \"%s\" / \"%s\"\n"
|
||||
, argv[0], pcv_opt, ptm_opt, save_opt
|
||||
, save_opt, mariko_ext, erista_ext);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool save_patched = false;
|
||||
char* exec_path = argv[2];
|
||||
if (argc == 4 && !strcmp(argv[2], save_opt)) {
|
||||
save_patched = true;
|
||||
exec_path = argv[3];
|
||||
}
|
||||
|
||||
size_t file_size;
|
||||
void* file_buffer = loadExec(exec_path, &file_size);
|
||||
|
||||
size_t exec_path_len = strlen(reinterpret_cast<const char *>(exec_path));
|
||||
size_t exec_path_patched_len = exec_path_len + std::max(strlen(mariko_ext), strlen(erista_ext)) + 1;
|
||||
|
||||
if (exe_opt == EXE_PCV) {
|
||||
ams::ldr::hoc::pcv::SafetyCheck();
|
||||
|
||||
{
|
||||
void* erista_buf = malloc(file_size);
|
||||
std::memcpy(erista_buf, file_buffer, file_size);
|
||||
|
||||
printf("Patching %s for Erista...\n", pcv_opt);
|
||||
ams::ldr::hoc::pcv::erista::Patch(reinterpret_cast<uintptr_t>(erista_buf), file_size);
|
||||
if (save_patched) {
|
||||
char* exec_path_erista = reinterpret_cast<char *>(malloc(exec_path_patched_len));
|
||||
strncpy(exec_path_erista, exec_path, exec_path_patched_len);
|
||||
strncat(exec_path_erista, erista_ext, exec_path_patched_len);
|
||||
saveExec(exec_path_erista, erista_buf, file_size);
|
||||
free(exec_path_erista);
|
||||
}
|
||||
free(erista_buf);
|
||||
}
|
||||
|
||||
{
|
||||
void* mariko_buf = malloc(file_size);
|
||||
std::memcpy(mariko_buf, file_buffer, file_size);
|
||||
|
||||
printf("Patching %s for Mariko...\n", pcv_opt);
|
||||
ams::ldr::hoc::pcv::mariko::Patch(reinterpret_cast<uintptr_t>(mariko_buf), file_size);
|
||||
if (save_patched) {
|
||||
char* exec_path_mariko = reinterpret_cast<char *>(malloc(exec_path_patched_len));
|
||||
strncpy(exec_path_mariko, exec_path, exec_path_patched_len);
|
||||
strncat(exec_path_mariko, mariko_ext, exec_path_patched_len);
|
||||
saveExec(exec_path_mariko, mariko_buf, file_size);
|
||||
free(exec_path_mariko);
|
||||
}
|
||||
free(mariko_buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (exe_opt == EXE_PTM) {
|
||||
void* mariko_buf = malloc(file_size);
|
||||
std::memcpy(mariko_buf, file_buffer, file_size);
|
||||
|
||||
printf("Patching %s (Mariko Only)...\n", ptm_opt);
|
||||
ams::ldr::hoc::ptm::Patch(reinterpret_cast<uintptr_t>(mariko_buf), file_size);
|
||||
if (save_patched) {
|
||||
char* exec_path_mariko = reinterpret_cast<char *>(malloc(exec_path_patched_len));
|
||||
strncpy(exec_path_mariko, exec_path, exec_path_patched_len);
|
||||
strncat(exec_path_mariko, mariko_ext, exec_path_patched_len);
|
||||
saveExec(exec_path_mariko, mariko_buf, file_size);
|
||||
free(exec_path_mariko);
|
||||
}
|
||||
free(mariko_buf);
|
||||
}
|
||||
|
||||
free(file_buffer);
|
||||
printf("Passed!\n\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
81
Source/Atmosphere/stratosphere/loader/source/oc/oc_test.hpp
Normal file
81
Source/Atmosphere/stratosphere/loader/source/oc/oc_test.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) Switch-OC-Suite
|
||||
*
|
||||
* 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
|
||||
|
||||
#ifndef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef int32_t s32;
|
||||
typedef uint64_t u64;
|
||||
typedef int Result;
|
||||
|
||||
#define R_SUCCEEDED(arg) (arg == 0)
|
||||
#define R_FAILED(arg) (arg != 0)
|
||||
#define LOGGING(fmt, ...) { printf(fmt "\n", ##__VA_ARGS__); }
|
||||
#define CRASH(msg, ...) { fprintf(stderr, "%s\nFailed in %s!\n", msg, __PRETTY_FUNCTION__); exit(-1); }
|
||||
#define R_SUCCEED() { return 0; }
|
||||
#define R_THROW(err) { return err; }
|
||||
#define R_TRY(expr) { Result _rc = (expr); if (R_FAILED(_rc)) { return _rc; } }
|
||||
#define R_UNLESS(expr, rc) { if (!(expr)) { return rc; } }
|
||||
|
||||
#define R_DEFINE_ERROR_RESULT(name, rc) \
|
||||
inline Result Result##name() { return rc; }
|
||||
|
||||
#define HEXDUMP(ptr, len) \
|
||||
{ \
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t *>(ptr); \
|
||||
size_t i, j; \
|
||||
for (i = 0; i < len; i += 16) { \
|
||||
printf("%06zx: ", i); \
|
||||
for (j = 0; j < 16 && i + j < len; j++) \
|
||||
printf("%02x ", p[i + j]); \
|
||||
for (; j < 16; j++) \
|
||||
printf(" "); \
|
||||
for (j = 0; j < 16 && i + j < len; j++) \
|
||||
printf("%c", isprint(p[i + j]) ? p[i + j] : '.'); \
|
||||
printf("\n"); \
|
||||
} \
|
||||
} \
|
||||
|
||||
typedef struct UnitTest {
|
||||
using Func = Result(*)();
|
||||
|
||||
const char* description;
|
||||
Func fun = nullptr;
|
||||
|
||||
void Test() {
|
||||
Result res = fun();
|
||||
if (R_FAILED(res)) {
|
||||
CRASH(description);
|
||||
}
|
||||
}
|
||||
} UnitTest;
|
||||
|
||||
#endif
|
||||
@@ -22,157 +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() {
|
||||
struct sValidator {
|
||||
volatile u32 value;
|
||||
u32 min;
|
||||
u32 max;
|
||||
u32 panic;
|
||||
bool value_required = false;
|
||||
|
||||
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) {
|
||||
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;
|
||||
if (min && value < min)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
if (max && value > max)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
sValidator validators[] = {
|
||||
{ C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true },
|
||||
{ C.marikoCpuBoostClock, 1020'000, 2703'000, panic::Cpu, true },
|
||||
{ C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, },
|
||||
{ C.marikoCpuMaxVolt, 1000, 1200, panic::Cpu, },
|
||||
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, },
|
||||
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000, panic::Cpu, },
|
||||
{ C.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, }, /* Official vmax for the RAMs is 1400-1500mV */
|
||||
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000, panic::Emc, },
|
||||
{ C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, },
|
||||
{ C.marikoEmcVddqVolt, 400'000, 750'000, panic::Emc, },
|
||||
{ C.marikoSocVmax, 1000, 1200, panic::Emc, },
|
||||
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, panic::Gpu, },
|
||||
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000, panic::Gpu, },
|
||||
{ C.marikoGpuVmax, 800, 960, panic::Gpu, },
|
||||
};
|
||||
|
||||
for (auto &v : validators) {
|
||||
if (R_FAILED(v.check())) {
|
||||
panic::SmcError(v.panic);
|
||||
CRASH("Validation FAIL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
SafetyCheck();
|
||||
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
if (isMariko) {
|
||||
mariko::Patch(mapped_nso, nso_size);
|
||||
};
|
||||
u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
|
||||
u32 marikoCpuDvfsMaxFreq;
|
||||
if (C.marikoCpuUVHigh) {
|
||||
marikoCpuDvfsMaxFreq = static_cast<u32>(
|
||||
GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq
|
||||
);
|
||||
} else {
|
||||
erista::Patch(mapped_nso, nso_size);
|
||||
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:
|
||||
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, 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,11 +22,275 @@
|
||||
|
||||
#include "../oc_common.hpp"
|
||||
#include "pcv_common.hpp"
|
||||
#include "pcv_erista.hpp"
|
||||
#include "pcv_mariko.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv {
|
||||
|
||||
namespace mariko {
|
||||
constexpr cvb_entry_t CpuCvbTableDefault[] = {
|
||||
{ 204000, { 721589, -12695, 27 }, { } },
|
||||
{ 306000, { 747134, -14195, 27 }, { } },
|
||||
{ 408000, { 776324, -15705, 27 }, { } },
|
||||
{ 510000, { 809160, -17205, 27 }, { } },
|
||||
{ 612000, { 845641, -18715, 27 }, { } },
|
||||
{ 714000, { 885768, -20215, 27 }, { } },
|
||||
{ 816000, { 929540, -21725, 27 }, { } },
|
||||
{ 918000, { 976958, -23225, 27 }, { } },
|
||||
{ 1020000, { 1028021, -24725, 27 }, { 1120000 } },
|
||||
{ 1122000, { 1082730, -26235, 27 }, { 1120000 } },
|
||||
{ 1224000, { 1141084, -27735, 27 }, { 1120000 } },
|
||||
{ 1326000, { 1203084, -29245, 27 }, { 1120000 } },
|
||||
{ 1428000, { 1268729, -30745, 27 }, { 1120000 } },
|
||||
{ 1581000, { 1374032, -33005, 27 }, { 1120000 } },
|
||||
{ 1683000, { 1448791, -34505, 27 }, { 1120000 } },
|
||||
{ 1785000, { 1527196, -36015, 27 }, { 1120000 } },
|
||||
{ 1887000, { 1609246, -37515, 27 }, { 1120000 } },
|
||||
{ 1963500, { 1675751, -38635, 27 }, { 1120000 } },
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 CpuClkOfficial = 1963'500;
|
||||
constexpr u32 CpuVoltOfficial = 1120;
|
||||
constexpr u32 CpuVminOfficial = 620;
|
||||
|
||||
static const u32 cpuVoltagePatchValues[] = { 850, 38, 1120, 1000, 100, 1000, 0 };
|
||||
static const s32 cpuVoltagePatchOffsets[] = { -2, -1, 5, 6, 7, 8, 9 };
|
||||
static_assert(sizeof(cpuVoltagePatchValues) == sizeof(cpuVoltagePatchOffsets), "Invalid cpuVoltagePatch size");
|
||||
|
||||
static const u32 cpuVoltThermalData[] = { 620, 1120, 20000, 620, 1120, 70000, 950, 1132, 0, 950, 1227, 0 };
|
||||
|
||||
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
|
||||
{ 76800, {}, { 610000, } },
|
||||
{ 153600, {}, { 610000, } },
|
||||
{ 230400, {}, { 610000, } },
|
||||
{ 307200, {}, { 610000, } },
|
||||
{ 384000, {}, { 610000, } },
|
||||
{ 460800, {}, { 610000, } },
|
||||
{ 537600, {}, { 801688, -10900, -163, 298, -10599, 162, } },
|
||||
{ 614400, {}, { 824214, -5743, -452, 238, -6325, 81, } },
|
||||
{ 691200, {}, { 848830, -3903, -552, 119, -4030, -2, } },
|
||||
{ 768000, {}, { 891575, -4409, -584, 0, -2849, 39, } },
|
||||
{ 844800, {}, { 940071, -5367, -602, -60, -63, -93, } },
|
||||
{ 921600, {}, { 986765, -6637, -614, -179, 1905, -13, } },
|
||||
{ 998400, {}, { 1098475, -13529, -497, -179, 3626, 9, } },
|
||||
{ 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40, } },
|
||||
{ 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110, } },
|
||||
{ 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313, } },
|
||||
{ 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559, } },
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 GpuClkPllMax = 1300'000'000;
|
||||
constexpr u32 GpuClkPllLimit = 2'600'000;
|
||||
constexpr u32 GpuVminOfficial = 610;
|
||||
|
||||
static const u32 gpuDVFSPattern[] = { 1050, 1000, 100, 1000, 10, };
|
||||
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)
|
||||
* sf | opc | | hw | imm16 | Rd
|
||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
|
||||
*
|
||||
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
|
||||
* sf | opc | | hw | imm16 | Rd
|
||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
|
||||
*/
|
||||
|
||||
inline constexpr u32 asm_pattern[] = {0x52820000, 0x72A001C0};
|
||||
|
||||
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) {
|
||||
return ((ins1 ^ ins2) >> 5) == 0;
|
||||
};
|
||||
|
||||
inline auto asm_get_rd = [](u32 ins) {
|
||||
return ins & ((1 << 5) - 1);
|
||||
};
|
||||
|
||||
inline auto asm_set_rd = [](u32 ins, u8 rd) {
|
||||
return (ins & 0xFFFFFFE0) | (rd & 0x1F);
|
||||
};
|
||||
|
||||
inline auto asm_set_imm16 = [](u32 ins, u16 imm) {
|
||||
return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5);
|
||||
};
|
||||
|
||||
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
|
||||
return asm_compare_no_rd(*ptr32, asm_pattern[0]);
|
||||
}
|
||||
|
||||
constexpr emc_dvb_dvfs_table_t EmcDvbTableDefault[] = {
|
||||
{ 204000, { 637, 637, 637, } },
|
||||
{ 408000, { 637, 637, 637, } },
|
||||
{ 800000, { 637, 637, 637, } },
|
||||
{ 1065600, { 637, 637, 637, } },
|
||||
{ 1331200, { 650, 637, 637, } },
|
||||
{ 1600000, { 675, 650, 637, } },
|
||||
};
|
||||
|
||||
constexpr u32 EmcClkOSAlt = 1331'200;
|
||||
constexpr u32 EmcClkPllmLimit = 2133'000'000;
|
||||
constexpr u32 EmcVddqDefault = 600'000;
|
||||
constexpr u32 MemVdd2Default = 1100'000;
|
||||
|
||||
constexpr u32 MTC_TABLE_REV = 3;
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
|
||||
}
|
||||
|
||||
namespace erista {
|
||||
static u32 maxEmcClocks[] = { C.eristaEmcMaxClock2, C.eristaEmcMaxClock1, C.eristaEmcMaxClock, };
|
||||
#define GET_MAX_OF_ARR(ARR) (*std::max_element(ARR, ARR + std::size(ARR)))
|
||||
|
||||
constexpr cvb_entry_t CpuCvbTableDefault[] = {
|
||||
// CPU_PLL_CVB_TABLE_ODN
|
||||
{ 204000, {721094}, { } },
|
||||
{ 306000, {754040}, { } },
|
||||
{ 408000, {786986}, { } },
|
||||
{ 510000, {819932}, { } },
|
||||
{ 612000, {852878}, { } },
|
||||
{ 714000, {885824}, { } },
|
||||
{ 816000, {918770}, { } },
|
||||
{ 918000, {951716}, { } },
|
||||
{ 1020000, {984662}, { -2875621, 358099, -8585} },
|
||||
{ 1122000, {1017608}, { -52225, 104159, -2816} },
|
||||
{ 1224000, {1050554}, { 1076868, 8356, -727} },
|
||||
{ 1326000, {1083500}, { 2208191, -84659, 1240} },
|
||||
{ 1428000, {1116446}, { 2519460, -105063, 1611} },
|
||||
{ 1581000, {1130000}, { 2889664, -122173, 1834} },
|
||||
{ 1683000, {1168000}, { 5100873, -279186, 4747} },
|
||||
{ 1785000, {1227500}, { 5100873, -279186, 4747} },
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 CpuVoltOfficial = 1235;
|
||||
|
||||
constexpr u32 CpuVminOfficial = 825;
|
||||
|
||||
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 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");
|
||||
constexpr u32 GpuClkPllLimit = 2'600'000;
|
||||
constexpr u32 GpuClkPllMax = 921'600'000;
|
||||
constexpr u32 GpuVminOfficial = 810;
|
||||
|
||||
static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, };
|
||||
static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern");
|
||||
|
||||
static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 };
|
||||
static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "invalid gpuVoltageThermalPattern size");
|
||||
|
||||
|
||||
/* GPU Max Clock asm Pattern:
|
||||
*
|
||||
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
|
||||
* sf | opc | | hw | imm16 | Rd
|
||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
|
||||
*
|
||||
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
|
||||
* sf | opc | | hw | imm16 | Rd
|
||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
|
||||
*/
|
||||
inline constexpr u32 asm_pattern[] = {
|
||||
0x52820000, 0x72A001C0
|
||||
};
|
||||
|
||||
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) {
|
||||
return ((ins1 ^ ins2) >> 5) == 0;
|
||||
};
|
||||
|
||||
inline auto asm_get_rd = [](u32 ins) {
|
||||
return ins & ((1 << 5) - 1);
|
||||
};
|
||||
|
||||
inline auto asm_set_rd = [](u32 ins, u8 rd) {
|
||||
return (ins & 0xFFFFFFE0) | (rd & 0x1F);
|
||||
};
|
||||
|
||||
inline auto asm_set_imm16 = [](u32 ins, u16 imm) {
|
||||
return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5);
|
||||
};
|
||||
|
||||
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
|
||||
return asm_compare_no_rd(*ptr32, asm_pattern[0]);
|
||||
};
|
||||
|
||||
constexpr cvb_entry_t GpuCvbTableDefault[] = {
|
||||
// NA_FREQ_CVB_TABLE
|
||||
{ 76800, {}, { 814294, 8144, -940, 808, -21583, 226, } },
|
||||
{ 153600, {}, { 856185, 8144, -940, 808, -21583, 226, } },
|
||||
{ 230400, {}, { 898077, 8144, -940, 808, -21583, 226, } },
|
||||
{ 307200, {}, { 939968, 8144, -940, 808, -21583, 226, } },
|
||||
{ 384000, {}, { 981860, 8144, -940, 808, -21583, 226, } },
|
||||
{ 460800, {}, { 1023751, 8144, -940, 808, -21583, 226, } },
|
||||
{ 537600, {}, { 1065642, 8144, -940, 808, -21583, 226, } },
|
||||
{ 614400, {}, { 1107534, 8144, -940, 808, -21583, 226, } },
|
||||
{ 691200, {}, { 1149425, 8144, -940, 808, -21583, 226, } },
|
||||
{ 768000, {}, { 1191317, 8144, -940, 808, -21583, 226, } },
|
||||
{ 844800, {}, { 1233208, 8144, -940, 808, -21583, 226, } },
|
||||
{ 921600, {}, { 1275100, 8144, -940, 808, -21583, 226, } },
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 MemVoltHOS = 1125'000;
|
||||
constexpr u32 EmcClkPllmLimit = 1866'000'000;
|
||||
|
||||
constexpr u32 MTC_TABLE_REV = 7;
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
}
|
||||
|
||||
inline auto MatchesPattern = [](u32 *base, const auto &offsets, const auto &values) {
|
||||
for (size_t i = 0; i < std::size(values); ++i) {
|
||||
if (*(base + offsets[i]) != values[i]) {
|
||||
@@ -118,34 +382,34 @@ namespace ams::ldr::hoc::pcv {
|
||||
cvb_entry_t *customize_table;
|
||||
if (isMariko) {
|
||||
switch (C.marikoGpuUV) {
|
||||
case 0:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
|
||||
break;
|
||||
case 1:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableSLT);
|
||||
break;
|
||||
case 2:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
|
||||
break;
|
||||
default:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
|
||||
break;
|
||||
case 1:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableSLT);
|
||||
break;
|
||||
case 2:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
|
||||
break;
|
||||
default:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (C.eristaGpuUV) {
|
||||
case 0:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
||||
break;
|
||||
case 1:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableSLT);
|
||||
break;
|
||||
case 2:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableHiOPT);
|
||||
break;
|
||||
default:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
||||
break;
|
||||
}
|
||||
case 0:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
||||
break;
|
||||
case 1:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableSLT);
|
||||
break;
|
||||
case 2:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableHiOPT);
|
||||
break;
|
||||
default:
|
||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t default_entry_count = GetDvfsTableEntryCount(default_table);
|
||||
@@ -195,6 +459,26 @@ namespace ams::ldr::hoc::pcv {
|
||||
Result MemFreqPllmLimit(u32 *ptr);
|
||||
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
|
||||
|
||||
template <typename T>
|
||||
Result MemMtcCustomizeTable(T *dst, T *src) {
|
||||
constexpr u32 mtc_magic = std::is_same_v<T, MarikoMtcTable> ? MARIKO_MTC_MAGIC : ERISTA_MTC_MAGIC;
|
||||
R_UNLESS(src->rev == mtc_magic, ldr::ResultInvalidMtcMagic());
|
||||
|
||||
constexpr u32 ZERO_VAL = UINT32_MAX;
|
||||
// Skip params from dvfs_ver to clock_src;
|
||||
for (size_t offset = offsetof(T, clk_src_emc); offset < sizeof(T); offset += sizeof(u32)) {
|
||||
u32 *src_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(src) + offset);
|
||||
u32 *dst_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(dst) + offset);
|
||||
u32 src_val = *src_ent;
|
||||
|
||||
if (src_val){
|
||||
PATCH_OFFSET(dst_ent, src_val == ZERO_VAL ? 0 : src_val);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
};
|
||||
|
||||
void SafetyCheck();
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Switch-OC-Suite
|
||||
*
|
||||
* Copyright (c) 2023 hanai3Bi
|
||||
*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::ldr::hoc::pcv {
|
||||
|
||||
constexpr u32 NopIns = 0x1f2003d5;
|
||||
|
||||
template <typename Compare>
|
||||
u32 *ScanAssembly(u32 *ptr, u32 scanLimit, u32 pattern, Compare comp) {
|
||||
for (u32 i = 0; i < scanLimit; ++i) {
|
||||
if (comp(pattern, ptr[i])) {
|
||||
return ptr + i;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) {
|
||||
return ((ins1 ^ ins2) >> 5) == 0;
|
||||
};
|
||||
|
||||
inline auto asm_get_rd = [](u32 ins) {
|
||||
return ins & ((1 << 5) - 1);
|
||||
};
|
||||
|
||||
inline auto asm_set_rd = [](u32 ins, u8 rd) {
|
||||
return (ins & 0xFFFFFFE0) | (rd & 0x1F);
|
||||
};
|
||||
|
||||
inline auto asm_set_imm16 = [](u32 ins, u16 imm) {
|
||||
return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5);
|
||||
};
|
||||
|
||||
inline auto AsmGetImm16 = [](u32 ins) {
|
||||
return static_cast<u16>((ins >> 5) & 0xFFFF);
|
||||
};
|
||||
|
||||
inline auto AsmCompareBrNoRd = [](u32 ins1, u32 ins2) {
|
||||
constexpr u32 RegMask = ~(((1 << 5) - 1) << 5);
|
||||
return ((ins1 & RegMask) ^ (ins2 & RegMask)) == 0;
|
||||
};
|
||||
|
||||
inline auto AsmCompareAddNoImm12 = [](u32 ins1, u32 ins2) {
|
||||
constexpr u32 Imm12Mask = ~(((1 << 12) - 1) << 10);
|
||||
return ((ins1 & Imm12Mask) ^ (ins2 & Imm12Mask)) == 0;
|
||||
};
|
||||
|
||||
inline auto AsmCompareAdrpNoImm = [](u32 ins1, u32 ins2) {
|
||||
constexpr u32 ImmMask = ~((((1 << 2) - 1) << 29) | (((1 << 19) - 1) << 5));
|
||||
return ((ins1 & ImmMask) ^ (ins2 & ImmMask)) == 0;
|
||||
};
|
||||
|
||||
/* Csel (Conditional Select) */
|
||||
/*
|
||||
SF | Op | S | | RM | Cond | 0 | 0 | Rn | Rd
|
||||
31 | 30 | 29 | 28 27 26 25 24 23 | 20 19 18 17 16 | 15 14 13 12 | 11 | 10 | 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
inline auto AsmCompareCselNoReg = [](u32 ins1, u32 ins2) {
|
||||
constexpr u32 ClearReg = ~(((1 << 10) - 1) | (((1 << 5) - 1) << 16));
|
||||
return ((ins1 & ClearReg) ^ (ins2 & ClearReg)) == 0;
|
||||
};
|
||||
|
||||
/* Mul */
|
||||
/*
|
||||
SF | Op54 | Op31 | RM | o0 | RA | RN | RD
|
||||
31 | 30 29 28 27 26 25 24 | 23 22 21 | 20 19 18 17 16 | 15 | 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
inline auto AsmCompareMullNoReg = [](u32 ins1, u32 ins2) {
|
||||
constexpr u32 ClearReg = ~(((1 << 10) - 1) | (((1 << 5) - 1) << 16));
|
||||
return ((ins1 & ClearReg) ^ (ins2 & ClearReg)) == 0;
|
||||
};
|
||||
|
||||
/* Mul */
|
||||
/* MUL W11, W24, W26 */
|
||||
/* multiplies by 1000, mV -> uV */
|
||||
/*
|
||||
SF | Op54 | Op31 | RM | o0 | RA | RN | RD
|
||||
31 | 30 29 28 27 26 25 24 | 23 22 21 | 20 19 18 17 16 | 15 | 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
inline auto AsmGetMullRn = [](u32 ins) {
|
||||
constexpr u32 Mask = ((1 << 5) - 1) << 5;
|
||||
return (ins & Mask) >> 5;
|
||||
};
|
||||
|
||||
inline auto AsmGetMullRm = [](u32 ins) {
|
||||
constexpr u32 Mask = ((1 << 5) - 1) << 16;
|
||||
return (ins & Mask) >> 16;
|
||||
};
|
||||
|
||||
/* Subs (Shifted register) */
|
||||
/*
|
||||
SF | Op | S | | Shift | 0 | RM | Imm6 | Rn | Rd
|
||||
31 | 30 | 29 | 28 27 26 25 24 | 23 22 | 21 | 20 19 18 17 16 | 15 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
inline auto AsmSubsSetRn = [](u32 ins, u8 rn) {
|
||||
constexpr u32 RnMaskClear = ~(((1u << 5) - 1u) << 5);
|
||||
constexpr u32 RnMaskSet = (1u << 5) - 1u;
|
||||
|
||||
return (ins & RnMaskClear) | ((static_cast<u32>(rn) & RnMaskSet) << 5);
|
||||
};
|
||||
|
||||
/* Subs (Immediate) */
|
||||
|
||||
/*
|
||||
SF | Op | S | | Sh | Imm12 | Rn | Rd
|
||||
31 | 30 | 29 | 28 27 26 25 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
inline auto AsmSubsSetImm12 = [](u32 ins, u16 imm12) {
|
||||
constexpr u32 ClearMask = ~(((1u << 12) - 1) << 10);
|
||||
constexpr u32 SetImm12Mask = ( 1u << 12) - 1;
|
||||
|
||||
return (ins & ClearMask) | ((imm12 & SetImm12Mask) << 10);
|
||||
};
|
||||
|
||||
inline auto AsmSubsCompareNoReg = [](u32 ins1, u32 ins2) {
|
||||
return ((ins1 ^ ins2) >> 10) == 0;
|
||||
};
|
||||
|
||||
inline auto AsmCompareBrConNoImm19 = [](u32 ins1, u32 ins2) {
|
||||
constexpr u32 ClearImm19 = ~(((1 << 19) - 1) << 5);
|
||||
return (ins1 & ClearImm19) == (ins2 & ClearImm19);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -16,170 +16,153 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "../mtc_timing_table.hpp"
|
||||
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;
|
||||
|
||||
struct cvb_coefficients {
|
||||
s32 c0 = 0;
|
||||
s32 c1 = 0;
|
||||
s32 c2 = 0;
|
||||
s32 c3 = 0;
|
||||
s32 c4 = 0;
|
||||
s32 c5 = 0;
|
||||
};
|
||||
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);
|
||||
|
||||
struct cvb_entry_t {
|
||||
u64 freq;
|
||||
cvb_coefficients cvb_dfll_param;
|
||||
cvb_coefficients cvb_pll_param;
|
||||
};
|
||||
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;
|
||||
|
||||
struct CvbCpuDfllData {
|
||||
u32 tune0_low;
|
||||
u32 tune0_high;
|
||||
u32 tune1_low;
|
||||
u32 tune1_high;
|
||||
u32 tune_high_min_millivolts;
|
||||
u32 tune_high_margin_millivolts;
|
||||
u64 dvco_calibration_max;
|
||||
};
|
||||
typedef struct emc_dvb_dvfs_table_t {
|
||||
u64 freq;
|
||||
s32 volt[4] = {0};
|
||||
} emc_dvb_dvfs_table_t;
|
||||
|
||||
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;
|
||||
};
|
||||
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;
|
||||
|
||||
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?
|
||||
};
|
||||
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;
|
||||
|
||||
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];
|
||||
};
|
||||
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;
|
||||
|
||||
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];
|
||||
};
|
||||
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);
|
||||
|
||||
struct __attribute__((packed)) CvbMeta {
|
||||
u64 socType;
|
||||
CvbCpuDfllData dfllData; /* Maybe? */
|
||||
u32 unkZero2[6];
|
||||
u32 unkMagic;
|
||||
u32 unkZero3;
|
||||
u32 highVmin;
|
||||
u32 unkStepMaybe;
|
||||
u32 vmin;
|
||||
u32 unkZero4[3];
|
||||
u32 pllMinMilliVolts;
|
||||
u32 vmax;
|
||||
u32 unkScale2;
|
||||
u32 speedoScale;
|
||||
u32 voltageScale;
|
||||
u32 unkZero5;
|
||||
};
|
||||
static_assert(sizeof(CvbMeta) == 0x78);
|
||||
constexpr u32 CpuClkOSLimit = 1785'000;
|
||||
|
||||
constexpr u32 CpuClkOSLimit = 1785'000;
|
||||
constexpr u32 GpuClkOsLimit = 921'600;
|
||||
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) {
|
||||
u8 *m = reinterpret_cast<u8 *>(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) {
|
||||
@@ -66,14 +66,13 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
}
|
||||
|
||||
Result CpuVoltDfll(u32* ptr) {
|
||||
CvbCpuDfllData *entry = reinterpret_cast<CvbCpuDfllData *>(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();
|
||||
}
|
||||
|
||||
@@ -101,32 +100,32 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
if (C.eristaGpuVmin) {
|
||||
PATCH_OFFSET(ptr, C.eristaGpuVmin);
|
||||
PATCH_OFFSET(ptr + 3, C.eristaGpuVmin);
|
||||
PATCH_OFFSET(ptr + 6, C.eristaGpuVmin);
|
||||
PATCH_OFFSET(ptr + 9, C.eristaGpuVmin);
|
||||
PATCH_OFFSET(ptr , C.eristaGpuVmin);
|
||||
PATCH_OFFSET(ptr + 3, C.eristaGpuVmin);
|
||||
PATCH_OFFSET(ptr + 6, C.eristaGpuVmin);
|
||||
PATCH_OFFSET(ptr + 9, C.eristaGpuVmin);
|
||||
PATCH_OFFSET(ptr + 12, C.eristaGpuVmin);
|
||||
}
|
||||
|
||||
@@ -136,15 +135,13 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
Result GpuFreqMaxAsm(u32 *ptr32) {
|
||||
// Check if both two instructions match the pattern
|
||||
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
|
||||
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) {
|
||||
if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1])))
|
||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||
}
|
||||
|
||||
// Both instructions should operate on the same register
|
||||
u8 rd = asm_get_rd(ins1);
|
||||
if (rd != asm_get_rd(ins2)) {
|
||||
if (rd != asm_get_rd(ins2))
|
||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||
}
|
||||
|
||||
u32 max_clock;
|
||||
switch (C.eristaGpuUV) {
|
||||
@@ -161,13 +158,10 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 asm_patch[2] = {
|
||||
asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd),
|
||||
asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)
|
||||
};
|
||||
|
||||
PATCH_OFFSET(ptr32, asm_patch[0]);
|
||||
asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd),
|
||||
asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)};
|
||||
PATCH_OFFSET(ptr32, asm_patch[0]);
|
||||
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -211,15 +205,13 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
}
|
||||
|
||||
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);
|
||||
trefbw = MIN(trefbw, static_cast<u32>(0x3FFF));
|
||||
|
||||
CalculateTimings(tCK_avg);
|
||||
|
||||
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
|
||||
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
|
||||
WRITE_PARAM_ALL_REG(table, emc_rc, MIN(GET_CYCLE_CEIL(tRC), static_cast<u32>(0xB9)));
|
||||
WRITE_PARAM_ALL_REG(table, emc_rc, MIN(GET_CYCLE_CEIL(tRC), static_cast<u32>(0xB8)));
|
||||
WRITE_PARAM_ALL_REG(table, emc_ras, MIN(GET_CYCLE_CEIL(tRAS), static_cast<u32>(0x7F)));
|
||||
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
|
||||
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb));
|
||||
@@ -242,6 +234,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
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);
|
||||
@@ -276,20 +269,22 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
// 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);
|
||||
// ams::ldr::hoc::pcv::mariko::CalculateMrw2();
|
||||
// table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast<u32>(mrw2);
|
||||
// table->dram_timings.rl = RL;
|
||||
|
||||
/* This needs some clean up. */
|
||||
constexpr double MC_ARB_DIV = 4.0;
|
||||
constexpr u32 MC_ARB_SFA = 2;
|
||||
constexpr u32 MC_ARB_SFA = 2;
|
||||
|
||||
table->burst_mc_regs.mc_emem_arb_cfg = table->rate_khz / (33.3 * 1000) / MC_ARB_DIV;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_cfg = table->rate_khz / (33.3 * 1000) / MC_ARB_DIV;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV) - 1;
|
||||
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(tR2P / MC_ARB_DIV);
|
||||
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(tW2P / MC_ARB_DIV) + MC_ARB_SFA;
|
||||
|
||||
@@ -325,8 +320,8 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
table->la_scale_regs.mc_ptsa_grant_decrement = grant_decrement;
|
||||
|
||||
constexpr u32 MaskHigh = 0xFF00FFFF;
|
||||
constexpr u32 Mask2 = 0xFFFFFF00;
|
||||
constexpr u32 Mask3 = 0xFF00FF00;
|
||||
constexpr u32 Mask2 = 0xFFFFFF00;
|
||||
constexpr u32 Mask3 = 0xFF00FF00;
|
||||
|
||||
const u32 allowance1 = static_cast<u32>(0x32000 / (table->rate_khz / 0x3E8)) & 0xFF;
|
||||
const u32 allowance2 = static_cast<u32>(0x9C40 / (table->rate_khz / 0x3E8)) & 0xFF;
|
||||
@@ -354,60 +349,50 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
table->la_scale_regs.mc_latency_allowance_hc_1 = (table->la_scale_regs.mc_latency_allowance_hc_1 & Mask2) | allowance1;
|
||||
table->la_scale_regs.mc_latency_allowance_vi2_0 = (table->la_scale_regs.mc_latency_allowance_vi2_0 & Mask2) | allowance1;
|
||||
|
||||
table->dram_timings.t_rp = tRFCpb;
|
||||
table->dram_timings.t_rp = tRFCpb;
|
||||
table->dram_timings.t_rfc = tRFCab;
|
||||
table->emc_cfg_2 = 0x11083D;
|
||||
table->min_volt = std::clamp(900 + (C.emcDvbShift * 25), 900, 1050);
|
||||
table->emc_cfg_2 = 0x11083D;
|
||||
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) {
|
||||
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);
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
if (GET_MAX_OF_ARR(maxEmcClocks) <= EmcClkOSLimit) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
/* 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 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++) {
|
||||
u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable);
|
||||
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());
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -422,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);
|
||||
@@ -437,38 +436,35 @@ 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) {
|
||||
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
||||
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
|
||||
|
||||
PatcherEntry<u32> patches[] = {
|
||||
{"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, CpuCvbDefaultMaxFreq },
|
||||
{"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, CpuVminOfficial },
|
||||
{"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial },
|
||||
{"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low },
|
||||
{"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial },
|
||||
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
|
||||
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq },
|
||||
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
|
||||
{"GPU PLL Max", & GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
|
||||
// {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
|
||||
{"MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit },
|
||||
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
|
||||
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
|
||||
{"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
|
||||
{"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq)},
|
||||
{"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},
|
||||
{"MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit},
|
||||
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit},
|
||||
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit},
|
||||
{"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS},
|
||||
};
|
||||
|
||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) {
|
||||
@@ -483,8 +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())) {
|
||||
panic::SmcError(panic::Patch);
|
||||
|
||||
CRASH(entry.description);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Switch-OC-Suite
|
||||
*
|
||||
* Copyright (c) 2023 hanai3Bi
|
||||
*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../oc_common.hpp"
|
||||
#include "pcv_common.hpp"
|
||||
#include "pcv_asm.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
static u32 maxEmcClocks[] = { C.eristaEmcMaxClock2, C.eristaEmcMaxClock1, C.eristaEmcMaxClock, };
|
||||
#define GET_MAX_OF_ARR(ARR) (*std::max_element(ARR, ARR + std::size(ARR)))
|
||||
|
||||
constexpr cvb_entry_t CpuCvbTableDefault[] = {
|
||||
// CPU_PLL_CVB_TABLE_ODN
|
||||
{ 204000, {721094}, { } },
|
||||
{ 306000, {754040}, { } },
|
||||
{ 408000, {786986}, { } },
|
||||
{ 510000, {819932}, { } },
|
||||
{ 612000, {852878}, { } },
|
||||
{ 714000, {885824}, { } },
|
||||
{ 816000, {918770}, { } },
|
||||
{ 918000, {951716}, { } },
|
||||
{ 1020000, {984662}, { -2875621, 358099, -8585} },
|
||||
{ 1122000, {1017608}, { -52225, 104159, -2816} },
|
||||
{ 1224000, {1050554}, { 1076868, 8356, -727} },
|
||||
{ 1326000, {1083500}, { 2208191, -84659, 1240} },
|
||||
{ 1428000, {1116446}, { 2519460, -105063, 1611} },
|
||||
{ 1581000, {1130000}, { 2889664, -122173, 1834} },
|
||||
{ 1683000, {1168000}, { 5100873, -279186, 4747} },
|
||||
{ 1785000, {1227500}, { 5100873, -279186, 4747} },
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 CpuVoltOfficial = 1227;
|
||||
constexpr u32 CpuVminOfficial = 825;
|
||||
constexpr u32 CpuTune0Low = 0xFFEAD0FF;
|
||||
|
||||
constexpr u32 CpuVoltL4T = 1257'000;
|
||||
|
||||
static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 };
|
||||
static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "Invalid cpuVoltDvfsPattern size");
|
||||
|
||||
static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 };
|
||||
static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "Invalid cpuVoltageThermalPattern size");
|
||||
|
||||
constexpr u32 GpuClkPllLimit = 2'600'000;
|
||||
constexpr u32 GpuClkPllMax = 921'600'000;
|
||||
constexpr u32 GpuVminOfficial = 810;
|
||||
|
||||
constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 };
|
||||
|
||||
static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, };
|
||||
static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern");
|
||||
|
||||
static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 };
|
||||
static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "Invalid gpuVoltageThermalPattern size");
|
||||
|
||||
/* GPU Max Clock asm Pattern:
|
||||
*
|
||||
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
|
||||
* sf | opc | | hw | imm16 | Rd
|
||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
|
||||
*
|
||||
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
|
||||
* sf | opc | | hw | imm16 | Rd
|
||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
|
||||
*/
|
||||
inline constexpr u32 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 };
|
||||
|
||||
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
|
||||
return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]);
|
||||
};
|
||||
|
||||
constexpr cvb_entry_t GpuCvbTableDefault[] = {
|
||||
// NA_FREQ_CVB_TABLE
|
||||
{ 76800, {}, { 814294, 8144, -940, 808, -21583, 226, } },
|
||||
{ 153600, {}, { 856185, 8144, -940, 808, -21583, 226, } },
|
||||
{ 230400, {}, { 898077, 8144, -940, 808, -21583, 226, } },
|
||||
{ 307200, {}, { 939968, 8144, -940, 808, -21583, 226, } },
|
||||
{ 384000, {}, { 981860, 8144, -940, 808, -21583, 226, } },
|
||||
{ 460800, {}, { 1023751, 8144, -940, 808, -21583, 226, } },
|
||||
{ 537600, {}, { 1065642, 8144, -940, 808, -21583, 226, } },
|
||||
{ 614400, {}, { 1107534, 8144, -940, 808, -21583, 226, } },
|
||||
{ 691200, {}, { 1149425, 8144, -940, 808, -21583, 226, } },
|
||||
{ 768000, {}, { 1191317, 8144, -940, 808, -21583, 226, } },
|
||||
{ 844800, {}, { 1233208, 8144, -940, 808, -21583, 226, } },
|
||||
{ 921600, {}, { 1275100, 8144, -940, 808, -21583, 226, } },
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 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;
|
||||
constexpr u32 MtcTableCountDefault = 10;
|
||||
|
||||
constexpr size_t MtcFullTableSize = sizeof(EristaMtcTable) * MtcTableCountDefault;
|
||||
constexpr u32 MtcFullTableCount = 3;
|
||||
|
||||
/* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
|
||||
enum DramId {
|
||||
ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH = 0,
|
||||
ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE = 1,
|
||||
ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC = 2,
|
||||
ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH = 4,
|
||||
ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX = 7,
|
||||
};
|
||||
|
||||
enum MtcTableIndex {
|
||||
T210SdevEmcDvfsTableS4gb01 = 0, /* HB-MGCH, WT:C */
|
||||
T210SdevEmcDvfsTableS6gb01 = 1, /* HM-MGCH */
|
||||
T210SdevEmcDvfsTableH4gb01 = 2, /* HR-NLE */
|
||||
MtcTableIndex_Invalid = 3,
|
||||
};
|
||||
|
||||
struct MtcDramIndex {
|
||||
DramId dramId;
|
||||
MtcTableIndex index;
|
||||
};
|
||||
|
||||
const inline MtcDramIndex mtcIndexTable[] = {
|
||||
{ ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, },
|
||||
{ ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC, T210SdevEmcDvfsTableS4gb01, },
|
||||
{ ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, },
|
||||
{ ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, },
|
||||
};
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,306 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Switch-OC-Suite
|
||||
*
|
||||
* Copyright (c) 2023 hanai3Bi
|
||||
*
|
||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../oc_common.hpp"
|
||||
#include "pcv_common.hpp"
|
||||
#include "pcv_asm.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
constexpr cvb_entry_t CpuCvbTableDefault[] = {
|
||||
{ 204000, { 721589, -12695, 27 }, { } },
|
||||
{ 306000, { 747134, -14195, 27 }, { } },
|
||||
{ 408000, { 776324, -15705, 27 }, { } },
|
||||
{ 510000, { 809160, -17205, 27 }, { } },
|
||||
{ 612000, { 845641, -18715, 27 }, { } },
|
||||
{ 714000, { 885768, -20215, 27 }, { } },
|
||||
{ 816000, { 929540, -21725, 27 }, { } },
|
||||
{ 918000, { 976958, -23225, 27 }, { } },
|
||||
{ 1020000, { 1028021, -24725, 27 }, { 1120000 } },
|
||||
{ 1122000, { 1082730, -26235, 27 }, { 1120000 } },
|
||||
{ 1224000, { 1141084, -27735, 27 }, { 1120000 } },
|
||||
{ 1326000, { 1203084, -29245, 27 }, { 1120000 } },
|
||||
{ 1428000, { 1268729, -30745, 27 }, { 1120000 } },
|
||||
{ 1581000, { 1374032, -33005, 27 }, { 1120000 } },
|
||||
{ 1683000, { 1448791, -34505, 27 }, { 1120000 } },
|
||||
{ 1785000, { 1527196, -36015, 27 }, { 1120000 } },
|
||||
{ 1887000, { 1609246, -37515, 27 }, { 1120000 } },
|
||||
{ 1963500, { 1675751, -38635, 27 }, { 1120000 } },
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 CpuClkOfficial = 1963'500;
|
||||
constexpr u32 CpuVoltOfficial = 1120;
|
||||
constexpr u32 CpuHighVminOfficial = 850;
|
||||
constexpr u32 CpuVminOfficial = 620;
|
||||
constexpr u32 CpuTune0Low = 0xFFCF;
|
||||
|
||||
static const u32 cpuVoltagePatchValues[] = { 850, 38, 1120, 1000, 100, 1000, 0 };
|
||||
static const s32 cpuVoltagePatchOffsets[] = { -2, -1, 5, 6, 7, 8, 9 };
|
||||
static_assert(sizeof(cpuVoltagePatchValues) == sizeof(cpuVoltagePatchOffsets), "Invalid cpuVoltagePatch size");
|
||||
|
||||
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, };
|
||||
|
||||
constexpr cvb_entry_t GpuCvbTableDefault[] = {
|
||||
// GPUB01_NA_CVB_TABLE
|
||||
{ 76800, {}, { 610000, } },
|
||||
{ 153600, {}, { 610000, } },
|
||||
{ 230400, {}, { 610000, } },
|
||||
{ 307200, {}, { 610000, } },
|
||||
{ 384000, {}, { 610000, } },
|
||||
{ 460800, {}, { 610000, } },
|
||||
{ 537600, {}, { 801688, -10900, -163, 298, -10599, 162, } },
|
||||
{ 614400, {}, { 824214, -5743, -452, 238, -6325, 81, } },
|
||||
{ 691200, {}, { 848830, -3903, -552, 119, -4030, -2, } },
|
||||
{ 768000, {}, { 891575, -4409, -584, 0, -2849, 39, } },
|
||||
{ 844800, {}, { 940071, -5367, -602, -60, -63, -93, } },
|
||||
{ 921600, {}, { 986765, -6637, -614, -179, 1905, -13, } },
|
||||
{ 998400, {}, { 1098475, -13529, -497, -179, 3626, 9, } },
|
||||
{ 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40, } },
|
||||
{ 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110, } },
|
||||
{ 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313, } },
|
||||
{ 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559, } },
|
||||
{ },
|
||||
};
|
||||
|
||||
constexpr u32 GpuClkPllMax = 1300'000'000;
|
||||
constexpr u32 GpuClkPllLimit = 2'600'000;
|
||||
constexpr u32 GpuVminOfficial = 610;
|
||||
|
||||
static const u32 gpuDVFSPattern[] = { 1050, 1000, 100, 1000, 10, };
|
||||
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");
|
||||
|
||||
/* GPU Max Clock asm Pattern:
|
||||
*
|
||||
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
|
||||
* sf | opc | | hw | imm16 | Rd
|
||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
|
||||
*
|
||||
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
|
||||
* sf | opc | | hw | imm16 | Rd
|
||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
|
||||
*/
|
||||
inline constexpr u32 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 };
|
||||
|
||||
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
|
||||
return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]);
|
||||
}
|
||||
|
||||
struct DvbEntry {
|
||||
u64 freq;
|
||||
u32 volt[4] = {};
|
||||
};
|
||||
|
||||
constexpr DvbEntry EmcDvbTableDefault[] = {
|
||||
{ 204000, { 637, 637, 637, } },
|
||||
{ 408000, { 637, 637, 637, } },
|
||||
{ 800000, { 637, 637, 637, } },
|
||||
{ 1065600, { 637, 637, 637, } },
|
||||
{ 1331200, { 650, 637, 637, } },
|
||||
{ 1600000, { 675, 650, 637, } },
|
||||
};
|
||||
|
||||
/* Movz */
|
||||
/*
|
||||
SF | OPC | HW | Imm16 | RD
|
||||
31 | 30 29 28 27 26 25 24 23 | 22 21 | 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
constexpr u32 SocVoltCompareSpeedoAsm = 0x7118FAFF; /* subs imm, compares to >=1598 max speedo and then goes down process id 1 route. */
|
||||
constexpr u32 SocVoltWriteProcessIdAsm = 0x2A1F03F4; /* orr, writes id 0. */
|
||||
constexpr u32 SocVoltWriteVoltageAsm = 0x52808358; /* Movz imm, writes 1050mV. */
|
||||
constexpr u32 SocVoltSelectRegisterAsm = 0x1A9A3118; /* Csel, selects the voltage -- we need the register of this. */
|
||||
constexpr u32 SocVoltMultiplyVoltsAsm = 0x1B1A7F0B; /* Mul, converts from mV -> uV */
|
||||
constexpr u32 SocVoltValidateLimitAsm = 0x6B0A017F; /* Subs, checks limits */
|
||||
constexpr u32 SocVoltBranchToAbortAsm = 0x540020AC; /* B.ge Branches to abort if limits are invalid. */
|
||||
|
||||
ALWAYS_INLINE bool SocVoltPatternFn(u32 *ptr) {
|
||||
return asm_compare_no_rd(*ptr, SocVoltCompareSpeedoAsm);
|
||||
}
|
||||
|
||||
constexpr u32 SocVoltLimitOfficial = 1050;
|
||||
constexpr u32 SocVoltLimitMaxDefaultIndex = 17;
|
||||
static const u32 socVoltLimitArray[DvfsTableEntryCount] = { 637, 650, 675, 700, 725, 750, 775, 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, };
|
||||
|
||||
constexpr u32 EmcListDefault[] = { 204000, 1331200, 1600000, };
|
||||
constexpr u32 EmcListSizeDefault = std::size(EmcListDefault);
|
||||
constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1;
|
||||
constexpr u32 EmcRateStep = 33'000;
|
||||
constexpr u32 EmcRateStepScale = 33'200;
|
||||
|
||||
constexpr u32 EmcClkOSAlt = 1331'200;
|
||||
constexpr u32 EmcClkPllmLimit = 2133'000'000;
|
||||
constexpr u32 EmcVddqDefault = 600'000;
|
||||
constexpr u32 MemVdd2Default = 1100'000;
|
||||
|
||||
constexpr u32 MTC_TABLE_REV = 3;
|
||||
constexpr u32 MtcTableCountDefault = 3;
|
||||
|
||||
constexpr size_t MtcFullTableSize = sizeof(MarikoMtcTable) * MtcTableCountDefault;
|
||||
constexpr u32 MtcFullTableCount = 17;
|
||||
|
||||
/* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
|
||||
enum DramId : u64 {
|
||||
HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 3,
|
||||
AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 5,
|
||||
IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 6,
|
||||
|
||||
IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 8,
|
||||
IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 9,
|
||||
IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 10,
|
||||
IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE = 11,
|
||||
|
||||
HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 12,
|
||||
HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 13,
|
||||
HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 14,
|
||||
HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE = 15,
|
||||
|
||||
IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 17,
|
||||
IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 18,
|
||||
HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 19,
|
||||
|
||||
IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 20,
|
||||
HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 21,
|
||||
AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 22,
|
||||
|
||||
HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 23,
|
||||
AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 24,
|
||||
|
||||
IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 25,
|
||||
HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF = 26,
|
||||
AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 27,
|
||||
|
||||
AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 28,
|
||||
|
||||
IOWA_4GB_HYNIX_H54G46CYRBX267 = 29,
|
||||
HOAG_4GB_HYNIX_H54G46CYRBX267 = 30,
|
||||
AULA_4GB_HYNIX_H54G46CYRBX267 = 31,
|
||||
|
||||
IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 32,
|
||||
HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB = 33,
|
||||
AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 34,
|
||||
};
|
||||
|
||||
enum MtcTableIndex {
|
||||
T210b0SdevEmcDvfsTableS4gb01 = 0, /* (Unused) Samsung 4Gb */
|
||||
T210b0SdevEmcDvfsTableS4gb03 = 1, /* Samsung AM-MGCJ 4Gb */
|
||||
T210b0SdevEmcDvfsTableS8gb03 = 2, /* (Unused) Samsung 4Gb */
|
||||
T210b0SdevEmcDvfsTableH4gb03 = 3, /* Hynix NME 4Gb */
|
||||
T210b0SdevEmcDvfsTableM4gb03 = 4, /* Micron WT:F 4Gb */
|
||||
T210b0SdevEmcDvfsTableS4gbY01 = 5, /* (Unused) Samsung 4Gb */
|
||||
T210b0SdevEmcDvfsTableS1y4gbY01 = 6, /* (Unused) Samsung 4Gb */
|
||||
T210b0SdevEmcDvfsTableS1y8gbY01 = 7, /* (Unused) Samsung 4Gb */
|
||||
T210b0SdevEmcDvfsTableS1y4gbX03 = 8, /* Samsung AA-MGCL 4Gb */
|
||||
T210b0SdevEmcDvfsTableS1y8gbX03 = 9, /* Samsung AA-MGCL 8Gb */
|
||||
T210b0SdevEmcDvfsTableS1y4gb01 = 10, /* (Unused) Samsung 4Gb */
|
||||
T210b0SdevEmcDvfsTableM1y4gb01 = 11, /* Micron WT:E 4Gb */
|
||||
T210b0SdevEmcDvfsTableH1y4gb01 = 12, /* Hynix NEE 4Gb */
|
||||
T210b0SdevEmcDvfsTableS1y8gb04 = 13, /* Samsung AM-MGCJ 8Gb */
|
||||
T210b0SdevEmcDvfsTableS1z4gb01 = 14, /* Samsung AB-MGCL 4Gb */
|
||||
T210b0SdevEmcDvfsTableH1a4gb01 = 15, /* Hynix x267 4Gb */
|
||||
T210b0SdevEmcDvfsTableM1a4gb01 = 16, /* Micron WT:B 8Gb */
|
||||
MtcTableIndex_Invalid = 17,
|
||||
};
|
||||
|
||||
struct MtcDramIndex {
|
||||
DramId dramId;
|
||||
MtcTableIndex index;
|
||||
};
|
||||
|
||||
const inline MtcDramIndex mtcIndexTable[] = {
|
||||
{ HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
|
||||
{ AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
|
||||
{ IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
|
||||
{ IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, T210b0SdevEmcDvfsTableS4gb03, },
|
||||
{ IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, T210b0SdevEmcDvfsTableS1y8gb04, },
|
||||
{ IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME, T210b0SdevEmcDvfsTableH4gb03, },
|
||||
{ IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE, T210b0SdevEmcDvfsTableM1y4gb01, },
|
||||
{ HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, T210b0SdevEmcDvfsTableS4gb03, },
|
||||
{ HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, T210b0SdevEmcDvfsTableS1y8gb04, },
|
||||
{ HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME, T210b0SdevEmcDvfsTableH4gb03, },
|
||||
{ HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE, T210b0SdevEmcDvfsTableM1y4gb01, },
|
||||
{ IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
|
||||
{ IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
|
||||
{ HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
|
||||
{ IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1z4gb01, },
|
||||
{ HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1y8gb04, },
|
||||
{ AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1y8gb04, },
|
||||
{ HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
|
||||
{ AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
|
||||
{ IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
|
||||
{ HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
|
||||
{ AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
|
||||
{ AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
|
||||
{ IOWA_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
|
||||
{ HOAG_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
|
||||
{ AULA_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
|
||||
{ IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
|
||||
{ HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
|
||||
{ AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
|
||||
};
|
||||
|
||||
/*
|
||||
710006abfc 40 01 1f d6 br x10
|
||||
*/
|
||||
|
||||
/*
|
||||
710006ac28 a0 03 00 90 adrp x0,0x71000de000
|
||||
710006ac2c 00 80 16 91 add x0=>SdevEmcDvfsTableS4gb01,x0,#0x5a0
|
||||
*/
|
||||
|
||||
/* Br */
|
||||
/*
|
||||
| Z | OP | Fixed | A | M | RN | RM
|
||||
31 30 29 28 27 26 25 | 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 |11 | 10 | 9 8 7 6 5 | 4 3 2 1 0
|
||||
1 1 0 1 0 1 1 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 Rn 0 0 0 0 0
|
||||
Z op A M Rm
|
||||
*/
|
||||
|
||||
/* Adrp */
|
||||
/*
|
||||
OP | ImmLow | | ImmHigh | RD
|
||||
31 | 30 29 | 28 27 26 25 24 | 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
|
||||
/* ADD (immediate) */
|
||||
/*
|
||||
SF | OP | S | Fixed value | Sh | Imm12 | RN | RD
|
||||
31 | 30 | 29 | 28 27 26 25 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
|
||||
constexpr u32 MtcBrAsm = 0xD61F0140;
|
||||
constexpr u32 MtcMovAsm = 0x52800068;
|
||||
constexpr u32 MtcAdrpAsm = 0x900003A0;
|
||||
constexpr u32 MtcAddAsm = 0x91168000;
|
||||
|
||||
ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) {
|
||||
/* This builds an address that gets returned, so the register must be x0 by convention. */
|
||||
return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
|
||||
}
|
||||
@@ -20,103 +20,116 @@
|
||||
|
||||
namespace ams::ldr::hoc::ptm {
|
||||
|
||||
Result CpuPtmBoost(perf_conf_entry* entry) {
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
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, };
|
||||
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
|
||||
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-r4
|
||||
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
|
||||
|
||||
@@ -65,7 +65,6 @@ IS_STATUS_MONITOR_DIRECTIVE := 1
|
||||
CFLAGS += -DIS_STATUS_MONITOR_DIRECTIVE=$(IS_STATUS_MONITOR_DIRECTIVE)
|
||||
|
||||
# Enable appearance overriding
|
||||
export MSYS2_ARG_CONV_EXCL := -DUI_OVERRIDE_PATH
|
||||
UI_OVERRIDE_PATH := /config/status-monitor/
|
||||
CFLAGS += -DUI_OVERRIDE_PATH="\"$(UI_OVERRIDE_PATH)\""
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Thanks to NaGa for Status Monitor Pro!
|
||||
Submodule Source/Horizon-OC-Monitor/lib/Atmosphere-libs updated: 82f1553c4c...6cc765fcaa
Submodule Source/Horizon-OC-Monitor/lib/libultrahand updated: 3e54f2a1ad...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);
|
||||
@@ -419,6 +424,8 @@ public:
|
||||
tsl::hlp::doWithSmSession([this]{
|
||||
|
||||
apmInitialize();
|
||||
if (hosversionAtLeast(8,0,0)) clkrstCheck = clkrstInitialize();
|
||||
else pcvCheck = pcvInitialize();
|
||||
|
||||
if (hosversionAtLeast(5,0,0)) tcCheck = tcInitialize();
|
||||
|
||||
@@ -439,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);
|
||||
@@ -459,18 +458,18 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
shmemClose(&_sharedmemory);
|
||||
//Exit services
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -498,6 +497,8 @@ public:
|
||||
//Initialize services
|
||||
tsl::hlp::doWithSmSession([this]{
|
||||
apmInitialize();
|
||||
if (hosversionAtLeast(8,0,0)) clkrstCheck = clkrstInitialize();
|
||||
else pcvCheck = pcvInitialize();
|
||||
|
||||
if (R_SUCCEEDED(nvInitialize())) nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu");
|
||||
|
||||
@@ -519,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);
|
||||
@@ -539,6 +532,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -546,10 +540,10 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
//Exit services
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -581,6 +575,8 @@ public:
|
||||
// Same service‐init as before
|
||||
tsl::hlp::doWithSmSession([this]{
|
||||
apmInitialize();
|
||||
if (hosversionAtLeast(8,0,0)) clkrstCheck = clkrstInitialize();
|
||||
else pcvCheck = pcvInitialize();
|
||||
|
||||
if (R_SUCCEEDED(nvInitialize())) nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu");
|
||||
|
||||
@@ -602,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);
|
||||
@@ -622,6 +610,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
|
||||
@@ -630,10 +619,10 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
// Exit services
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -667,6 +656,8 @@ public:
|
||||
virtual void initServices() override {
|
||||
tsl::hlp::doWithSmSession([this]{
|
||||
apmInitialize();
|
||||
if (hosversionAtLeast(8,0,0)) clkrstCheck = clkrstInitialize();
|
||||
else pcvCheck = pcvInitialize();
|
||||
|
||||
if (R_SUCCEEDED(nvInitialize())) nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu");
|
||||
|
||||
@@ -688,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);
|
||||
@@ -708,6 +691,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -715,9 +699,9 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -746,6 +730,8 @@ public:
|
||||
virtual void initServices() override {
|
||||
tsl::hlp::doWithSmSession([this]{
|
||||
apmInitialize();
|
||||
if (hosversionAtLeast(8,0,0)) clkrstCheck = clkrstInitialize();
|
||||
else pcvCheck = pcvInitialize();
|
||||
|
||||
if (R_SUCCEEDED(nvInitialize())) nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu");
|
||||
|
||||
@@ -767,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);
|
||||
@@ -787,6 +765,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -794,9 +773,9 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -825,6 +804,8 @@ public:
|
||||
virtual void initServices() override {
|
||||
tsl::hlp::doWithSmSession([this]{
|
||||
apmInitialize();
|
||||
if (hosversionAtLeast(8,0,0)) clkrstCheck = clkrstInitialize();
|
||||
else pcvCheck = pcvInitialize();
|
||||
|
||||
if (R_SUCCEEDED(nvInitialize())) nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu");
|
||||
|
||||
@@ -846,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);
|
||||
@@ -866,6 +839,7 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -873,9 +847,9 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -957,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,7 +141,7 @@ public:
|
||||
}
|
||||
});
|
||||
|
||||
// tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
//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);
|
||||
|
||||
|
||||
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,17 +20,9 @@ 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] = "";
|
||||
char RAM_load2_c[64] = "";
|
||||
char RAM_bw_peak_c[16] = "";
|
||||
char RAM_bw_total_c[16] = "";
|
||||
char RAM_bw_gpu_c[16] = "";
|
||||
char RAM_bw_cpu_c[16] = "";
|
||||
char Resolutions_c[64] = "";
|
||||
char readSpeed_c[32] = "";
|
||||
|
||||
@@ -125,7 +117,7 @@ public:
|
||||
|
||||
//Print strings
|
||||
///CPU
|
||||
if (1) {
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
||||
|
||||
uint32_t height_offset = 155;
|
||||
if (realCPU_Hz && settings.showRealFreqs) {
|
||||
@@ -173,7 +165,7 @@ public:
|
||||
}
|
||||
|
||||
///GPU
|
||||
if (R_SUCCEEDED(nvCheck)) {
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck) || R_SUCCEEDED(nvCheck)) {
|
||||
|
||||
uint32_t height_offset = 320-8;
|
||||
if (realGPU_Hz && settings.showRealFreqs) {
|
||||
@@ -181,7 +173,7 @@ public:
|
||||
}
|
||||
|
||||
renderer->drawString("GPU Usage", false, COMMON_MARGIN, 285-8, 20, (settings.catColor1));
|
||||
if (1) {
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
||||
if (settings.showTargetFreqs) {
|
||||
//static auto targetFreqWidth = renderer->getTextDimensions("Target Frequency: ", false, 15).first;
|
||||
renderer->drawString("Target Frequency", false, COMMON_MARGIN, height_offset, 15, (settings.catColor2));
|
||||
@@ -213,7 +205,7 @@ public:
|
||||
static std::vector<std::string> specialChars = {""};
|
||||
|
||||
///RAM
|
||||
if (R_SUCCEEDED(Hinted)) {
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck) || R_SUCCEEDED(Hinted)) {
|
||||
|
||||
uint32_t height_offset = 410;
|
||||
if (realRAM_Hz && settings.showRealFreqs) {
|
||||
@@ -221,7 +213,7 @@ public:
|
||||
}
|
||||
|
||||
renderer->drawString("RAM Usage", false, COMMON_MARGIN, 375, 20, (settings.catColor1));
|
||||
if (1) {
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
||||
if (settings.showTargetFreqs) {
|
||||
//static auto targetFreqWidth = renderer->getTextDimensions("Target Frequency: ", false, 15).first;
|
||||
renderer->drawString("Target Frequency", false, COMMON_MARGIN, height_offset, 15, (settings.catColor2));
|
||||
@@ -241,41 +233,16 @@ 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)) {
|
||||
if (settings.ramInfoMode == "Bandwidth") {
|
||||
// Fixed column layout: labels at fixed x, values at fixed x after max label width
|
||||
// Col1: Peak/GPU labels, Col2: Total/CPU labels
|
||||
// Values start at a fixed offset so columns don't shift with different digit counts
|
||||
static const uint32_t bwLbl1W = renderer->getTextDimensions("Peak ", false, 15).first;
|
||||
static const uint32_t bwLbl2W = renderer->getTextDimensions("Total ", false, 15).first;
|
||||
static const uint32_t bwValW = renderer->getTextDimensions("99.9 GB/s", false, 15).first;
|
||||
static const uint32_t bwGap = renderer->getTextDimensions(" ", false, 15).first;
|
||||
const uint32_t xV1 = COMMON_MARGIN + bwLbl1W;
|
||||
const uint32_t xL2 = xV1 + bwValW + bwGap;
|
||||
const uint32_t xV2 = xL2 + bwLbl2W;
|
||||
// Row 1: Peak ... Total
|
||||
renderer->drawString("Peak", false, COMMON_MARGIN, height_offset+15, 15, settings.catColor2);
|
||||
renderer->drawString(RAM_bw_peak_c, false, xV1, height_offset+15, 15, settings.textColor);
|
||||
renderer->drawString("Total", false, xL2, height_offset+15, 15, settings.catColor2);
|
||||
renderer->drawString(RAM_bw_total_c, false, xV2, height_offset+15, 15, settings.textColor);
|
||||
// Row 2: GPU ... CPU
|
||||
renderer->drawString("GPU", false, COMMON_MARGIN, height_offset+30, 15, settings.catColor2);
|
||||
renderer->drawString(RAM_bw_gpu_c, false, xV1, height_offset+30, 15, settings.textColor);
|
||||
renderer->drawString("CPU", false, xL2, height_offset+30, 15, settings.catColor2);
|
||||
renderer->drawString(RAM_bw_cpu_c, false, xV2, height_offset+30, 15, settings.textColor);
|
||||
} else {
|
||||
static std::vector<std::string> partLoadColoredChars = {"CPU", "GPU"};
|
||||
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)) {
|
||||
const uint32_t ramUsageOffset = (R_SUCCEEDED(hocclkCheck) && settings.ramInfoMode == "Bandwidth") ? height_offset + 50 : height_offset + 40;
|
||||
//static auto textWidth = renderer->getTextDimensions("Total \nApplication \nApplet \nSystem \nSystem Unsafe ", false, 15).first;
|
||||
renderer->drawString("Total\nApplication\nApplet\nSystem\nSystem Unsafe", false, COMMON_MARGIN, ramUsageOffset, 15, (settings.catColor2));
|
||||
renderer->drawString(RAM_var_compressed_c, false, COMMON_MARGIN + valueOffset, ramUsageOffset, 15, (settings.textColor));
|
||||
renderer->drawString(RAM_percentage_var_compressed_c, false, ramPercentageOffset, ramUsageOffset, 15, (settings.textColor));
|
||||
renderer->drawString("Total\nApplication\nApplet\nSystem\nSystem Unsafe", false, COMMON_MARGIN, height_offset + 40, 15, (settings.catColor2));
|
||||
renderer->drawString(RAM_var_compressed_c, false, COMMON_MARGIN + valueOffset, height_offset + 40, 15, (settings.textColor));
|
||||
renderer->drawString(RAM_percentage_var_compressed_c, false, ramPercentageOffset, height_offset + 40, 15, (settings.textColor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,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) {
|
||||
@@ -511,28 +443,13 @@ public:
|
||||
RAMPct_systemunsafe
|
||||
);
|
||||
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
if (settings.ramInfoMode == "Bandwidth") {
|
||||
const unsigned bwAll = partLoad[HocClkPartLoad_RamBWAll] / 1000;
|
||||
const unsigned bwAllD = (partLoad[HocClkPartLoad_RamBWAll] % 1000) / 100;
|
||||
const unsigned bwPeak = partLoad[HocClkPartLoad_RamBWPeak] / 1000;
|
||||
const unsigned bwPeakD= (partLoad[HocClkPartLoad_RamBWPeak]% 1000) / 100;
|
||||
const unsigned bwCpu = partLoad[HocClkPartLoad_RamBWCpu] / 1000;
|
||||
const unsigned bwCpuD = (partLoad[HocClkPartLoad_RamBWCpu] % 1000) / 100;
|
||||
const unsigned bwGpu = partLoad[HocClkPartLoad_RamBWGpu] / 1000;
|
||||
const unsigned bwGpuD = (partLoad[HocClkPartLoad_RamBWGpu] % 1000) / 100;
|
||||
snprintf(RAM_bw_peak_c, sizeof RAM_bw_peak_c, "%u.%u GB/s", bwPeak, bwPeakD);
|
||||
snprintf(RAM_bw_total_c, sizeof RAM_bw_total_c, "%u.%u GB/s", bwAll, bwAllD);
|
||||
snprintf(RAM_bw_gpu_c, sizeof RAM_bw_gpu_c, "%u.%u GB/s", bwGpu, bwGpuD);
|
||||
snprintf(RAM_bw_cpu_c, sizeof RAM_bw_cpu_c, "%u.%u GB/s", bwCpu, bwCpuD);
|
||||
} else {
|
||||
const int RAM_GPU_Load = partLoad[HocClkPartLoad_EMC] - partLoad[HocClkPartLoad_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,
|
||||
RAM_GPU_Load / 10, RAM_GPU_Load % 10);
|
||||
}
|
||||
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[SysClkPartLoad_EMC] / 10, partLoad[SysClkPartLoad_EMC] % 10,
|
||||
partLoad[SysClkPartLoad_EMCCpu] / 10, partLoad[SysClkPartLoad_EMCCpu] % 10,
|
||||
RAM_GPU_Load / 10, RAM_GPU_Load % 10);
|
||||
}
|
||||
///Thermal
|
||||
snprintf(SOC_temperature_c, sizeof SOC_temperature_c, "%.1f\u00B0C", SOC_temperatureF);
|
||||
@@ -541,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,7 +121,7 @@ public:
|
||||
|
||||
});
|
||||
|
||||
// tsl::elm::g_disableMenuCacheOnReturn.store(true, std::memory_order_release);
|
||||
//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);
|
||||
|
||||
|
||||
@@ -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,81 +0,0 @@
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
include $(DEVKITARM)/base_rules
|
||||
################################################################################
|
||||
IPL_LOAD_ADDR := 0x40010000
|
||||
################################################################################
|
||||
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||
BUILDDIR := build
|
||||
OUTPUTDIR := out
|
||||
SOURCEDIR = fatal_handler
|
||||
BDKDIR := bdk
|
||||
GFXDIR := fatal_handler/gfx
|
||||
TARGET := fatal_handler
|
||||
BDKINC := -I$(BDKDIR)
|
||||
GFXINC := -I$(GFXDIR)
|
||||
GFX_INC := '"gfx.h"'
|
||||
VPATH = $(dir ./$(SOURCEDIR)/) $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/*/))
|
||||
VPATH += $(dir $(wildcard ./$(BDKDIR)/)) $(dir $(wildcard ./$(BDKDIR)/*/)) $(dir $(wildcard ./$(BDKDIR)/*/*/))
|
||||
OBJS = $(addprefix $(BUILDDIR)/$(TARGET)/, \
|
||||
start.o exception_handlers.o irq.o main.o \
|
||||
timer.o heap.o hw_init.o clock.o i2c.o gpio.o \
|
||||
max7762x.o di.o util.o fuse.o pinmux.o \
|
||||
secmon_exo.o gfx.o bpmp.o sdram.o minerva.o btn.o max77620-rtc.o se.o mc.o)
|
||||
################################################################################
|
||||
# BDK defines.
|
||||
CUSTOMDEFINES := -DGFX_INC=$(GFX_INC)
|
||||
CUSTOMDEFINES += -DBDK_MALLOC_NO_DEFRAG -DBDK_MC_ENABLE_AHB_REDIRECT -DBDK_EMUMMC_ENABLE
|
||||
CUSTOMDEFINES += -DBDK_WATCHDOG_FIQ_ENABLE -DBDK_RESTART_BL_ON_WDT
|
||||
INCDIRS := $(BDKINC) $(GFXINC)
|
||||
WARNINGS := -Wall -Wsign-compare -Wno-array-bounds -Wno-stringop-overflow
|
||||
ARCH := -march=armv4t -mtune=arm7tdmi -mthumb-interwork
|
||||
CFLAGS = $(ARCH) -O2 -g -nostdlib -ffunction-sections -fdata-sections -fomit-frame-pointer -std=gnu11 $(WARNINGS) $(CUSTOMDEFINES)
|
||||
LDFLAGS = $(ARCH) -nostartfiles -lgcc -Wl,--nmagic,--gc-sections -Xlinker --defsym=IPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
|
||||
################################################################################
|
||||
|
||||
BIN_TARGET := $(OUTPUTDIR)/$(TARGET).bin
|
||||
BIN_OBJ := $(OUTPUTDIR)/$(TARGET).bin.o
|
||||
BIN_HEADER := $(OUTPUTDIR)/$(TARGET)_bin.h
|
||||
BIN_SYM := $(subst .,_,$(subst /,_,$(TARGET)))_bin
|
||||
|
||||
define bin2o
|
||||
$(eval SYM := $(BIN_SYM))
|
||||
$(eval BIN_SIZE := $(shell wc -c < $(BIN_TARGET)))
|
||||
@$(OBJCOPY) --input-target binary --output-target elf32-littlearm --binary-architecture arm \
|
||||
--redefine-sym _binary_$(shell echo $(BIN_TARGET) | tr '/.-' '___')_start=$(SYM) \
|
||||
--redefine-sym _binary_$(shell echo $(BIN_TARGET) | tr '/.-' '___')_end=$(SYM)_end \
|
||||
--redefine-sym _binary_$(shell echo $(BIN_TARGET) | tr '/.-' '___')_size=$(SYM)_size \
|
||||
$(BIN_TARGET) $(BIN_OBJ)
|
||||
@printf '#pragma once\n#include <stddef.h>\n#include <stdint.h>\nextern const uint8_t $(SYM)[];\nextern const uint8_t $(SYM)_end[];\n#if __cplusplus >= 201103L\nstatic constexpr size_t $(SYM)_size=$(BIN_SIZE);\n#else\nstatic const size_t $(SYM)_size=$(BIN_SIZE);\n#endif\n' > $(BIN_HEADER)
|
||||
endef
|
||||
|
||||
################################################################################
|
||||
.PHONY: all clean
|
||||
all: $(BIN_OBJ) $(BIN_HEADER)
|
||||
|
||||
$(BIN_OBJ) $(BIN_HEADER): $(BIN_TARGET)
|
||||
$(bin2o)
|
||||
|
||||
$(BIN_TARGET): $(BUILDDIR)/$(TARGET)/$(TARGET).elf
|
||||
$(OBJCOPY) -S -O binary $< $(BIN_TARGET)
|
||||
|
||||
clean:
|
||||
@rm -rf $(OBJS) $(BIN_OBJ) $(BIN_HEADER)
|
||||
|
||||
$(BUILDDIR)/$(TARGET)/$(TARGET).elf: $(OBJS)
|
||||
@$(CC) $(LDFLAGS) -T $(SOURCEDIR)/link.ld $^ -o $@
|
||||
|
||||
$(BUILDDIR)/$(TARGET)/%.o: %.c
|
||||
@$(CC) $(CFLAGS) $(INCDIRS) -c $< -o $@
|
||||
|
||||
$(BUILDDIR)/$(TARGET)/%.o: %.S
|
||||
@$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(OBJS): $(BUILDDIR)/$(TARGET)
|
||||
|
||||
$(BUILDDIR)/$(TARGET):
|
||||
@mkdir -p "$(BUILDDIR)"
|
||||
@mkdir -p "$(BUILDDIR)/$(TARGET)"
|
||||
@mkdir -p "$(OUTPUTDIR)"
|
||||
@@ -1,558 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2022 CTCaer
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "gfx.h"
|
||||
|
||||
// Global gfx console and context.
|
||||
gfx_ctxt_t gfx_ctxt;
|
||||
gfx_con_t gfx_con;
|
||||
|
||||
static bool gfx_con_init_done = false;
|
||||
|
||||
static const u8 _gfx_font[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 032 ( )
|
||||
0x00, 0x30, 0x30, 0x18, 0x18, 0x00, 0x0C, 0x00, // Char 033 (!)
|
||||
0x00, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, // Char 034 (")
|
||||
0x00, 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, // Char 035 (#)
|
||||
0x00, 0x18, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x18, // Char 036 ($)
|
||||
0x00, 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, // Char 037 (%)
|
||||
0x00, 0x3C, 0x66, 0x3C, 0x1C, 0xE6, 0x66, 0xFC, // Char 038 (&)
|
||||
0x00, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, // Char 039 (')
|
||||
0x00, 0x30, 0x18, 0x0C, 0x0C, 0x18, 0x30, 0x00, // Char 040 (()
|
||||
0x00, 0x0C, 0x18, 0x30, 0x30, 0x18, 0x0C, 0x00, // Char 041 ())
|
||||
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // Char 042 (*)
|
||||
0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // Char 043 (+)
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 044 (,)
|
||||
0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // Char 045 (-)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // Char 046 (.)
|
||||
0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, // Char 047 (/)
|
||||
0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00, // Char 048 (0)
|
||||
0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00, // Char 049 (1)
|
||||
0x00, 0x3C, 0x62, 0x30, 0x0C, 0x06, 0x7E, 0x00, // Char 050 (2)
|
||||
0x00, 0x3C, 0x62, 0x38, 0x60, 0x66, 0x3C, 0x00, // Char 051 (3)
|
||||
0x00, 0x6C, 0x6C, 0x66, 0xFE, 0x60, 0x60, 0x00, // Char 052 (4)
|
||||
0x00, 0x7E, 0x06, 0x7E, 0x60, 0x66, 0x3C, 0x00, // Char 053 (5)
|
||||
0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00, // Char 054 (6)
|
||||
0x00, 0x7E, 0x30, 0x30, 0x18, 0x18, 0x18, 0x00, // Char 055 (7)
|
||||
0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // Char 056 (8)
|
||||
0x00, 0x3C, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, // Char 057 (9)
|
||||
0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, // Char 058 (:)
|
||||
0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 059 (;)
|
||||
0x00, 0x70, 0x1C, 0x06, 0x06, 0x1C, 0x70, 0x00, // Char 060 (<)
|
||||
0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // Char 061 (=)
|
||||
0x00, 0x0E, 0x38, 0x60, 0x60, 0x38, 0x0E, 0x00, // Char 062 (>)
|
||||
0x00, 0x3C, 0x66, 0x30, 0x18, 0x00, 0x18, 0x00, // Char 063 (?)
|
||||
0x00, 0x3C, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3C, // Char 064 (@)
|
||||
0x00, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 065 (A)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 066 (B)
|
||||
0x00, 0x3C, 0x66, 0x06, 0x06, 0x66, 0x3C, 0x00, // Char 067 (C)
|
||||
0x00, 0x1E, 0x36, 0x66, 0x66, 0x36, 0x1E, 0x00, // Char 068 (D)
|
||||
0x00, 0x7E, 0x06, 0x1E, 0x06, 0x06, 0x7E, 0x00, // Char 069 (E)
|
||||
0x00, 0x3E, 0x06, 0x1E, 0x06, 0x06, 0x06, 0x00, // Char 070 (F)
|
||||
0x00, 0x3C, 0x66, 0x06, 0x76, 0x66, 0x3C, 0x00, // Char 071 (G)
|
||||
0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 072 (H)
|
||||
0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 073 (I)
|
||||
0x00, 0x78, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, // Char 074 (J)
|
||||
0x00, 0x66, 0x36, 0x1E, 0x1E, 0x36, 0x66, 0x00, // Char 075 (K)
|
||||
0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, // Char 076 (L)
|
||||
0x00, 0x46, 0x6E, 0x7E, 0x56, 0x46, 0x46, 0x00, // Char 077 (M)
|
||||
0x00, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00, // Char 078 (N)
|
||||
0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 079 (O)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00, // Char 080 (P)
|
||||
0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x70, 0x00, // Char 081 (Q)
|
||||
0x00, 0x3E, 0x66, 0x3E, 0x1E, 0x36, 0x66, 0x00, // Char 082 (R)
|
||||
0x00, 0x3C, 0x66, 0x0C, 0x30, 0x66, 0x3C, 0x00, // Char 083 (S)
|
||||
0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 084 (T)
|
||||
0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 085 (U)
|
||||
0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 086 (V)
|
||||
0x00, 0x46, 0x46, 0x56, 0x7E, 0x6E, 0x46, 0x00, // Char 087 (W)
|
||||
0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // Char 088 (X)
|
||||
0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // Char 089 (Y)
|
||||
0x00, 0x7E, 0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, // Char 090 (Z)
|
||||
0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, // Char 091 ([)
|
||||
0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // Char 092 (\)
|
||||
0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, // Char 093 (])
|
||||
0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, // Char 094 (^)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // Char 095 (_)
|
||||
0x00, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // Char 096 (`)
|
||||
0x00, 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x7C, 0x00, // Char 097 (a)
|
||||
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 098 (b)
|
||||
0x00, 0x00, 0x3C, 0x06, 0x06, 0x06, 0x3C, 0x00, // Char 099 (c)
|
||||
0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, // Char 100 (d)
|
||||
0x00, 0x00, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00, // Char 101 (e)
|
||||
0x00, 0x38, 0x0C, 0x3E, 0x0C, 0x0C, 0x0C, 0x00, // Char 102 (f)
|
||||
0x00, 0x00, 0x7C, 0x66, 0x7C, 0x40, 0x3C, 0x00, // Char 103 (g)
|
||||
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x00, // Char 104 (h)
|
||||
0x00, 0x18, 0x00, 0x1C, 0x18, 0x18, 0x3C, 0x00, // Char 105 (i)
|
||||
0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x1E, 0x00, // Char 106 (j)
|
||||
0x00, 0x06, 0x06, 0x36, 0x1E, 0x36, 0x66, 0x00, // Char 107 (k)
|
||||
0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 108 (l)
|
||||
0x00, 0x00, 0x66, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, // Char 109 (m)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, // Char 110 (n)
|
||||
0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 111 (o)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x00, // Char 112 (p)
|
||||
0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x00, // Char 113 (q)
|
||||
0x00, 0x00, 0x3E, 0x66, 0x06, 0x06, 0x06, 0x00, // Char 114 (r)
|
||||
0x00, 0x00, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x00, // Char 115 (s)
|
||||
0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x70, 0x00, // Char 116 (t)
|
||||
0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, // Char 117 (u)
|
||||
0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 118 (v)
|
||||
0x00, 0x00, 0xC6, 0xD6, 0xFE, 0x7C, 0x6C, 0x00, // Char 119 (w)
|
||||
0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // Char 120 (x)
|
||||
0x00, 0x00, 0x66, 0x66, 0x7C, 0x60, 0x3C, 0x00, // Char 121 (y)
|
||||
0x00, 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x7E, 0x00, // Char 122 (z)
|
||||
0x00, 0x18, 0x08, 0x08, 0x04, 0x08, 0x08, 0x18, // Char 123 ({)
|
||||
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // Char 124 (|)
|
||||
0x00, 0x0C, 0x08, 0x08, 0x10, 0x08, 0x08, 0x0C, // Char 125 (})
|
||||
0x00, 0x00, 0x00, 0x4C, 0x32, 0x00, 0x00, 0x00 // Char 126 (~)
|
||||
};
|
||||
|
||||
void gfx_clear_grey(u8 color)
|
||||
{
|
||||
memset(gfx_ctxt.fb, color, gfx_ctxt.width * gfx_ctxt.height * 4);
|
||||
}
|
||||
|
||||
void gfx_clear_partial_grey(u8 color, u32 pos_x, u32 height)
|
||||
{
|
||||
memset(gfx_ctxt.fb + pos_x * gfx_ctxt.stride, color, height * 4 * gfx_ctxt.stride);
|
||||
}
|
||||
|
||||
void gfx_clear_color(u32 color)
|
||||
{
|
||||
for (u32 i = 0; i < gfx_ctxt.width * gfx_ctxt.height; i++)
|
||||
gfx_ctxt.fb[i] = color;
|
||||
}
|
||||
|
||||
void gfx_init_ctxt(u32 *fb, u32 width, u32 height, u32 stride)
|
||||
{
|
||||
gfx_ctxt.fb = fb;
|
||||
gfx_ctxt.width = width;
|
||||
gfx_ctxt.height = height;
|
||||
gfx_ctxt.stride = stride;
|
||||
}
|
||||
|
||||
void gfx_con_init()
|
||||
{
|
||||
gfx_con.gfx_ctxt = &gfx_ctxt;
|
||||
gfx_con.fntsz = 16;
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y = 0;
|
||||
gfx_con.savedx = 0;
|
||||
gfx_con.savedy = 0;
|
||||
gfx_con.fgcol = TXT_CLR_DEFAULT;
|
||||
gfx_con.fillbg = 1;
|
||||
gfx_con.bgcol = TXT_CLR_BG;
|
||||
gfx_con.mute = 0;
|
||||
|
||||
gfx_con_init_done = true;
|
||||
}
|
||||
|
||||
void gfx_con_setcol(u32 fgcol, int fillbg, u32 bgcol)
|
||||
{
|
||||
gfx_con.fgcol = fgcol;
|
||||
gfx_con.fillbg = fillbg;
|
||||
gfx_con.bgcol = bgcol;
|
||||
}
|
||||
|
||||
void gfx_con_getpos(u32 *x, u32 *y)
|
||||
{
|
||||
*x = gfx_con.x;
|
||||
*y = gfx_con.y;
|
||||
}
|
||||
|
||||
void gfx_con_setpos(u32 x, u32 y)
|
||||
{
|
||||
gfx_con.x = x;
|
||||
gfx_con.y = y;
|
||||
}
|
||||
|
||||
void gfx_putc(char c)
|
||||
{
|
||||
// Duplicate code for performance reasons.
|
||||
switch (gfx_con.fntsz)
|
||||
{
|
||||
case 16:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)];
|
||||
u32 *fb = gfx_ctxt.fb + gfx_con.x + gfx_con.y * gfx_ctxt.stride;
|
||||
|
||||
for (u32 i = 0; i < 16; i += 2)
|
||||
{
|
||||
u8 v = *cbuf;
|
||||
for (u32 k = 0; k < 2; k++)
|
||||
{
|
||||
for (u32 j = 0; j < 8; j++)
|
||||
{
|
||||
if (v & 1)
|
||||
{
|
||||
*fb = gfx_con.fgcol;
|
||||
fb++;
|
||||
*fb = gfx_con.fgcol;
|
||||
}
|
||||
else if (gfx_con.fillbg)
|
||||
{
|
||||
*fb = gfx_con.bgcol;
|
||||
fb++;
|
||||
*fb = gfx_con.bgcol;
|
||||
}
|
||||
else
|
||||
fb++;
|
||||
v >>= 1;
|
||||
fb++;
|
||||
}
|
||||
fb += gfx_ctxt.stride - 16;
|
||||
v = *cbuf;
|
||||
}
|
||||
cbuf++;
|
||||
}
|
||||
gfx_con.x += 16;
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y += 16;
|
||||
if (gfx_con.y > gfx_ctxt.height - 16)
|
||||
gfx_con.y = 0;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)];
|
||||
u32 *fb = gfx_ctxt.fb + gfx_con.x + gfx_con.y * gfx_ctxt.stride;
|
||||
for (u32 i = 0; i < 8; i++)
|
||||
{
|
||||
u8 v = *cbuf++;
|
||||
for (u32 j = 0; j < 8; j++)
|
||||
{
|
||||
if (v & 1)
|
||||
*fb = gfx_con.fgcol;
|
||||
else if (gfx_con.fillbg)
|
||||
*fb = gfx_con.bgcol;
|
||||
v >>= 1;
|
||||
fb++;
|
||||
}
|
||||
fb += gfx_ctxt.stride - 8;
|
||||
}
|
||||
gfx_con.x += 8;
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
gfx_con.x = 0;
|
||||
gfx_con.y += 8;
|
||||
if (gfx_con.y > gfx_ctxt.height - 8)
|
||||
gfx_con.y = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_puts(const char *s)
|
||||
{
|
||||
if (!s || !gfx_con_init_done || gfx_con.mute)
|
||||
return;
|
||||
|
||||
for (; *s; s++)
|
||||
gfx_putc(*s);
|
||||
}
|
||||
|
||||
static void _gfx_putn(u32 v, int base, char fill, int fcnt)
|
||||
{
|
||||
static const char digits[] = "0123456789ABCDEF";
|
||||
|
||||
char *p;
|
||||
char buf[65];
|
||||
int c = fcnt;
|
||||
bool negative = false;
|
||||
|
||||
if (base != 10 && base != 16)
|
||||
return;
|
||||
|
||||
// Account for negative numbers.
|
||||
if (base == 10 && v & 0x80000000)
|
||||
{
|
||||
negative = true;
|
||||
v = (int)v * -1;
|
||||
c--;
|
||||
}
|
||||
|
||||
p = buf + 64;
|
||||
*p = 0;
|
||||
do
|
||||
{
|
||||
c--;
|
||||
*--p = digits[v % base];
|
||||
v /= base;
|
||||
} while (v);
|
||||
|
||||
if (negative)
|
||||
*--p = '-';
|
||||
|
||||
if (fill != 0)
|
||||
{
|
||||
while (c > 0 && p > buf)
|
||||
{
|
||||
*--p = fill;
|
||||
c--;
|
||||
}
|
||||
}
|
||||
|
||||
gfx_puts(p);
|
||||
}
|
||||
|
||||
void gfx_put_small_sep()
|
||||
{
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 8;
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
void gfx_put_big_sep()
|
||||
{
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 16;
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
void gfx_printf(const char *fmt, ...)
|
||||
{
|
||||
if (!gfx_con_init_done || gfx_con.mute)
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
int fill, fcnt;
|
||||
|
||||
va_start(ap, fmt);
|
||||
while (*fmt)
|
||||
{
|
||||
if (*fmt == '%')
|
||||
{
|
||||
fmt++;
|
||||
fill = 0;
|
||||
fcnt = 0;
|
||||
if ((*fmt >= '0' && *fmt <= '9') || *fmt == ' ')
|
||||
{
|
||||
fcnt = *fmt;
|
||||
fmt++;
|
||||
if (*fmt >= '0' && *fmt <= '9')
|
||||
{
|
||||
fill = fcnt;
|
||||
fcnt = *fmt - '0';
|
||||
fmt++;
|
||||
}
|
||||
else
|
||||
{
|
||||
fill = ' ';
|
||||
fcnt -= '0';
|
||||
}
|
||||
}
|
||||
switch(*fmt)
|
||||
{
|
||||
case 'c':
|
||||
gfx_putc(va_arg(ap, u32));
|
||||
break;
|
||||
case 's':
|
||||
gfx_puts(va_arg(ap, char *));
|
||||
break;
|
||||
case 'd':
|
||||
_gfx_putn(va_arg(ap, u32), 10, fill, fcnt);
|
||||
break;
|
||||
case 'p':
|
||||
case 'P':
|
||||
case 'x':
|
||||
case 'X':
|
||||
_gfx_putn(va_arg(ap, u32), 16, fill, fcnt);
|
||||
break;
|
||||
case 'k':
|
||||
gfx_con.fgcol = va_arg(ap, u32);
|
||||
break;
|
||||
case 'K':
|
||||
gfx_con.bgcol = va_arg(ap, u32);
|
||||
gfx_con.fillbg = 1;
|
||||
break;
|
||||
case '%':
|
||||
gfx_putc('%');
|
||||
break;
|
||||
case '\0':
|
||||
goto out;
|
||||
default:
|
||||
gfx_putc('%');
|
||||
gfx_putc(*fmt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
gfx_putc(*fmt);
|
||||
fmt++;
|
||||
}
|
||||
|
||||
out:
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void _gfx_cputs(u32 color, const char *s)
|
||||
{
|
||||
gfx_con.fgcol = color;
|
||||
gfx_puts(s);
|
||||
gfx_putc('\n');
|
||||
gfx_con.fgcol = TXT_CLR_DEFAULT;
|
||||
}
|
||||
|
||||
void gfx_wputs(const char *s) { _gfx_cputs(TXT_CLR_WARNING, s); }
|
||||
void gfx_eputs(const char *s) { _gfx_cputs(TXT_CLR_ERROR, s); }
|
||||
|
||||
void gfx_hexdump(u32 base, const void *buf, u32 len)
|
||||
{
|
||||
if (!gfx_con_init_done || gfx_con.mute)
|
||||
return;
|
||||
|
||||
u8 *buff = (u8 *)buf;
|
||||
|
||||
u8 prevFontSize = gfx_con.fntsz;
|
||||
gfx_con.fntsz = 8;
|
||||
for (u32 i = 0; i < len; i++)
|
||||
{
|
||||
if (i % 0x10 == 0)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
gfx_puts("| ");
|
||||
for (u32 j = 0; j < 0x10; j++)
|
||||
{
|
||||
u8 c = buff[i - 0x10 + j];
|
||||
if (c >= 32 && c <= 126)
|
||||
gfx_putc(c);
|
||||
else
|
||||
gfx_putc('.');
|
||||
}
|
||||
gfx_putc('\n');
|
||||
}
|
||||
gfx_printf("%08x: ", base + i);
|
||||
}
|
||||
gfx_printf("%02x ", buff[i]);
|
||||
if (i == len - 1)
|
||||
{
|
||||
int ln = len % 0x10 != 0;
|
||||
u32 k = 0x10 - 1;
|
||||
if (ln)
|
||||
{
|
||||
k = (len & 0xF) - 1;
|
||||
for (u32 j = 0; j < 0x10 - k; j++)
|
||||
gfx_puts(" ");
|
||||
}
|
||||
gfx_puts("| ");
|
||||
for (u32 j = 0; j < (ln ? k : k + 1); j++)
|
||||
{
|
||||
u8 c = buff[i - k + j];
|
||||
if (c >= 32 && c <= 126)
|
||||
gfx_putc(c);
|
||||
else
|
||||
gfx_putc('.');
|
||||
}
|
||||
gfx_putc('\n');
|
||||
}
|
||||
}
|
||||
gfx_putc('\n');
|
||||
gfx_con.fntsz = prevFontSize;
|
||||
}
|
||||
|
||||
static int abs(int x)
|
||||
{
|
||||
if (x < 0)
|
||||
return -x;
|
||||
return x;
|
||||
}
|
||||
|
||||
void gfx_set_pixel(u32 x, u32 y, u32 color)
|
||||
{
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = color;
|
||||
}
|
||||
|
||||
void gfx_line(int x0, int y0, int x1, int y1, u32 color)
|
||||
{
|
||||
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = (dx > dy ? dx : -dy) / 2, e2;
|
||||
|
||||
while (1)
|
||||
{
|
||||
gfx_set_pixel(x0, y0, color);
|
||||
if (x0 == x1 && y0 == y1)
|
||||
break;
|
||||
e2 = err;
|
||||
if (e2 >-dx)
|
||||
{
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dy)
|
||||
{
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_set_rect_grey(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 pos = 0;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
{
|
||||
memset(&gfx_ctxt.fb[x + y*gfx_ctxt.stride], buf[pos], 4);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void gfx_set_rect_rgb(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 pos = 0;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
{
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = buf[pos + 2] | (buf[pos + 1] << 8) | (buf[pos] << 16);
|
||||
pos+=3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
u32 *ptr = (u32 *)buf;
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = *ptr++;
|
||||
}
|
||||
|
||||
void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
|
||||
{
|
||||
for (u32 y = pos_y; y < (pos_y + size_y); y++)
|
||||
{
|
||||
for (u32 x = pos_x; x < (pos_x + size_x); x++)
|
||||
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = buf[(size_y + pos_y - 1 - y ) * size_x + x - pos_x];
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
* Copyright (c) 2018-2021 CTCaer
|
||||
* Copyright (c) 2018 M4xw
|
||||
*
|
||||
* 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 _GFX_H_
|
||||
#define _GFX_H_
|
||||
|
||||
#include <bdk.h>
|
||||
|
||||
#define TXT_CLR_BG 0xFF1B1B1B // Dark Grey.
|
||||
#define TXT_CLR_DEFAULT 0xFFCCCCCC // Light Grey.
|
||||
#define TXT_CLR_WARNING 0xFFFFDD00 // Yellow.
|
||||
#define TXT_CLR_ERROR 0xFFFF0000 // Red.
|
||||
#define TXT_CLR_CYAN_L 0xFF00CCFF // Light Cyan.
|
||||
#define TXT_CLR_TURQUOISE 0xFF00FFCC // Turquoise.
|
||||
#define TXT_CLR_ORANGE 0xFFFFBA00 // Orange.
|
||||
#define TXT_CLR_GREENISH 0xFF96FF00 // Toxic Green.
|
||||
#define TXT_CLR_GREEN_D 0xFF008800 // Dark Green.
|
||||
#define TXT_CLR_RED_D 0xFF880000 // Dark Red.
|
||||
#define TXT_CLR_GREY_D 0xFF303030 // Darkest Grey.
|
||||
#define TXT_CLR_GREY_DM 0xFF444444 // Darker Grey.
|
||||
#define TXT_CLR_GREY_M 0xFF555555 // Dark Grey.
|
||||
#define TXT_CLR_GREY 0xFF888888 // Grey.
|
||||
|
||||
#define EPRINTF(text) gfx_eputs(text)
|
||||
#define EPRINTFARGS(text, args...) gfx_printf("%k"text"%k\n", TXT_CLR_ERROR, args, TXT_CLR_DEFAULT)
|
||||
#define WPRINTF(text) gfx_wputs(text)
|
||||
#define WPRINTFARGS(text, args...) gfx_printf("%k"text"%k\n", TXT_CLR_WARNING, args, TXT_CLR_DEFAULT)
|
||||
|
||||
typedef struct _gfx_ctxt_t
|
||||
{
|
||||
u32 *fb;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 stride;
|
||||
} gfx_ctxt_t;
|
||||
|
||||
typedef struct _gfx_con_t
|
||||
{
|
||||
gfx_ctxt_t *gfx_ctxt;
|
||||
u32 fntsz;
|
||||
u32 x;
|
||||
u32 y;
|
||||
u32 savedx;
|
||||
u32 savedy;
|
||||
u32 fgcol;
|
||||
int fillbg;
|
||||
u32 bgcol;
|
||||
bool mute;
|
||||
} gfx_con_t;
|
||||
|
||||
// Global gfx console and context.
|
||||
extern gfx_ctxt_t gfx_ctxt;
|
||||
extern gfx_con_t gfx_con;
|
||||
|
||||
void gfx_init_ctxt(u32 *fb, u32 width, u32 height, u32 stride);
|
||||
void gfx_clear_grey(u8 color);
|
||||
void gfx_clear_partial_grey(u8 color, u32 pos_x, u32 height);
|
||||
void gfx_clear_color(u32 color);
|
||||
void gfx_con_init();
|
||||
void gfx_con_setcol(u32 fgcol, int fillbg, u32 bgcol);
|
||||
void gfx_con_getpos(u32 *x, u32 *y);
|
||||
void gfx_con_setpos(u32 x, u32 y);
|
||||
void gfx_putc(char c);
|
||||
void gfx_puts(const char *s);
|
||||
void gfx_wputs(const char *s);
|
||||
void gfx_eputs(const char *s);
|
||||
void gfx_printf(const char *fmt, ...) /* __attribute__((format(printf, 1, 2))) */;
|
||||
void gfx_hexdump(u32 base, const void *buf, u32 len);
|
||||
|
||||
void gfx_set_pixel(u32 x, u32 y, u32 color);
|
||||
void gfx_line(int x0, int y0, int x1, int y1, u32 color);
|
||||
void gfx_put_small_sep();
|
||||
void gfx_put_big_sep();
|
||||
void gfx_set_rect_grey(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_set_rect_rgb(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
|
||||
|
||||
#endif
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2024 CTCaer
|
||||
* Copyright (c) 2019 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 <bdk.h>
|
||||
#include "secmon_exo.h"
|
||||
|
||||
// Atmosphère reboot-to-fatal-error.
|
||||
typedef struct _atm_fatal_error_ctx
|
||||
{
|
||||
u32 magic;
|
||||
u32 error_desc;
|
||||
u64 title_id;
|
||||
union
|
||||
{
|
||||
u64 gprs[32];
|
||||
struct
|
||||
{
|
||||
u64 _gprs[29];
|
||||
u64 fp;
|
||||
u64 lr;
|
||||
u64 sp;
|
||||
};
|
||||
};
|
||||
u64 pc;
|
||||
u64 module_base;
|
||||
u32 pstate;
|
||||
u32 afsr0;
|
||||
u32 afsr1;
|
||||
u32 esr;
|
||||
u64 far;
|
||||
u64 report_identifier; // Normally just system tick.
|
||||
u64 stack_trace_size;
|
||||
u64 stack_dump_size;
|
||||
u64 stack_trace[0x20];
|
||||
u8 stack_dump[0x100];
|
||||
u8 tls[0x100];
|
||||
} atm_fatal_error_ctx;
|
||||
|
||||
|
||||
|
||||
#define ATM_FATAL_ERR_CTX_ADDR 0x4003E000
|
||||
#define ATM_FATAL_MAGIC 0x30454641 // AFE0
|
||||
|
||||
#define HOS_PID_BOOT2 0x8
|
||||
|
||||
|
||||
static const char *get_error_desc(u32 error_desc)
|
||||
{
|
||||
switch (error_desc)
|
||||
{
|
||||
case 0x100:
|
||||
return "IABRT"; // Instruction Abort.
|
||||
case 0x101:
|
||||
return "DABRT"; // Data Abort.
|
||||
case 0x102:
|
||||
return "IUA"; // Instruction Unaligned Access.
|
||||
case 0x103:
|
||||
return "DUA"; // Data Unaligned Access.
|
||||
case 0x104:
|
||||
return "UDF"; // Undefined Instruction.
|
||||
case 0x106:
|
||||
return "SYS"; // System Error.
|
||||
case 0x301:
|
||||
return "SVC"; // Bad arguments or unimplemented SVC.
|
||||
case 0xF00:
|
||||
return "KRNL"; // Kernel panic.
|
||||
case 0xFFD:
|
||||
return "SO"; // Stack Overflow.
|
||||
case 0xFFE:
|
||||
return "std::abort";
|
||||
default:
|
||||
return "UNK";
|
||||
}
|
||||
}
|
||||
|
||||
void secmon_exo_check_panic()
|
||||
{
|
||||
volatile atm_fatal_error_ctx *rpt = (atm_fatal_error_ctx *)ATM_FATAL_ERR_CTX_ADDR;
|
||||
|
||||
// Mask magic to maintain compatibility with any AFE version, thanks to additive struct members.
|
||||
if ((rpt->magic & 0xF0FFFFFF) != ATM_FATAL_MAGIC)
|
||||
return;
|
||||
|
||||
gfx_clear_grey(0x1B);
|
||||
gfx_con_setpos(0, 0);
|
||||
|
||||
WPRINTF("Panic occurred while running Atmosphere.\n\n");
|
||||
WPRINTFARGS("Title ID: %08X%08X", (u32)((u64)rpt->title_id >> 32), (u32)rpt->title_id);
|
||||
WPRINTFARGS("Error: %s (0x%x)\n", get_error_desc(rpt->error_desc), rpt->error_desc);
|
||||
|
||||
// Check if mixed atmosphere sysmodules.
|
||||
if ((u32)rpt->title_id == HOS_PID_BOOT2)
|
||||
WPRINTF("Mismatched Atmosphere files?\n");
|
||||
|
||||
|
||||
// Change magic to invalid, to prevent double-display of error/bootlooping.
|
||||
rpt->magic = 0;
|
||||
|
||||
display_backlight_brightness(100, 1000);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
PROVIDE(__ipl_start = IPL_LOAD_ADDR);
|
||||
. = __ipl_start;
|
||||
.text : {
|
||||
*(.text._start);
|
||||
KEEP(*(._boot_cfg));
|
||||
KEEP(*(._ipl_version));
|
||||
*(.text._irq_setup);
|
||||
*(.text*);
|
||||
}
|
||||
.data : {
|
||||
*(.data*);
|
||||
*(.rodata*);
|
||||
}
|
||||
. = ALIGN(0x10);
|
||||
__ipl_end = .;
|
||||
.bss : {
|
||||
__bss_start = .;
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
__bss_end = .;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
*
|
||||
* Copyright (c) 2018-2024 CTCaer
|
||||
*
|
||||
* 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 <bdk.h>
|
||||
#include "hos/secmon_exo.h"
|
||||
|
||||
typedef struct _log_ctx
|
||||
{
|
||||
u32 magic;
|
||||
u32 sz;
|
||||
u32 start;
|
||||
u32 end;
|
||||
char buf[];
|
||||
} log_ctx_t;
|
||||
|
||||
#define IRAM_LOG_CTX_ADDR 0x4003C000
|
||||
|
||||
static void check_log(){
|
||||
volatile log_ctx_t *log_ctx = (log_ctx_t*)IRAM_LOG_CTX_ADDR;
|
||||
|
||||
if(log_ctx->magic == 0xaabbccdd){
|
||||
gfx_printf("\nLogs:\n");
|
||||
gfx_printf((char*)log_ctx->buf);
|
||||
}
|
||||
}
|
||||
|
||||
volatile nyx_storage_t *nyx_str = (nyx_storage_t *)NYX_STORAGE_ADDR;
|
||||
|
||||
extern void pivot_stack(u32 stack_top);
|
||||
|
||||
void ipl_main()
|
||||
{
|
||||
// Do initial HW configuration. This is compatible with consecutive reruns without a reset.
|
||||
hw_init();
|
||||
// Pivot the stack under IPL. (Only max 4KB is needed).
|
||||
pivot_stack(IPL_LOAD_ADDR);
|
||||
|
||||
// Place heap at a place outside of L4T/HOS configuration and binaries.
|
||||
heap_init((void *)IPL_HEAP_START);
|
||||
|
||||
// Prep RTC regs for read. Needed for T210B01 R2C.
|
||||
max77620_rtc_prep_read();
|
||||
|
||||
// Initialize display.
|
||||
display_init();
|
||||
|
||||
u32 *fb = display_init_window_a_pitch();
|
||||
gfx_init_ctxt(fb, 720, 1280, 720);
|
||||
gfx_con_init();
|
||||
|
||||
// Initialize backlight PWM.
|
||||
display_backlight_pwm_init();
|
||||
display_backlight_brightness(100, 0);
|
||||
|
||||
// Show AMS errors
|
||||
secmon_exo_check_panic();
|
||||
check_log();
|
||||
|
||||
|
||||
gfx_printf("\n\nPress POWER to power off\nPress VOLUME to boot RCM\n");
|
||||
msleep(250);
|
||||
|
||||
|
||||
do{
|
||||
u8 btn = btn_read();
|
||||
if(btn & BTN_POWER){
|
||||
power_set_state(POWER_OFF);
|
||||
}
|
||||
if(btn & (BTN_VOL_DOWN | BTN_VOL_UP)){
|
||||
power_set_state(REBOOT_RCM);
|
||||
}
|
||||
}while(true);
|
||||
|
||||
// Halt BPMP if we managed to get out of execution.
|
||||
while (true)
|
||||
bpmp_halt();
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 naehrwert
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
.section .text._start
|
||||
.arm
|
||||
|
||||
.extern _reloc_ipl
|
||||
.type _reloc_ipl, %function
|
||||
|
||||
.extern memset
|
||||
.type memset, %function
|
||||
|
||||
.extern _irq_setup
|
||||
.type _irq_setup, %function
|
||||
|
||||
.globl _start
|
||||
.type _start, %function
|
||||
_start:
|
||||
ADR R0, _start
|
||||
LDR R1, =__ipl_start
|
||||
CMP R0, R1
|
||||
BEQ _real_start
|
||||
|
||||
/* If we are not in the right location already, copy a relocator to upper IRAM. */
|
||||
ADR R2, _reloc_ipl
|
||||
LDR R3, =0x4003FF00
|
||||
MOV R4, #(_real_start - _reloc_ipl)
|
||||
_copy_loop:
|
||||
LDMIA R2!, {R5}
|
||||
STMIA R3!, {R5}
|
||||
SUBS R4, #4
|
||||
BNE _copy_loop
|
||||
|
||||
/* Use the relocator to copy ourselves into the right place. */
|
||||
LDR R2, =__ipl_end
|
||||
SUB R2, R2, R1
|
||||
LDR R3, =_real_start
|
||||
LDR R4, =0x4003FF00
|
||||
BX R4
|
||||
|
||||
_reloc_ipl:
|
||||
LDMIA R0!, {R4-R7}
|
||||
STMIA R1!, {R4-R7}
|
||||
SUBS R2, #0x10
|
||||
BNE _reloc_ipl
|
||||
/* Jump to the relocated entry. */
|
||||
BX R3
|
||||
|
||||
_real_start:
|
||||
/* Initially, we place our stack under relocator but will move it to under the payload. */
|
||||
/* This depends on application scope. */
|
||||
LDR SP, =0x4003FF00
|
||||
LDR R0, =__bss_start
|
||||
EOR R1, R1, R1
|
||||
LDR R2, =__bss_end
|
||||
SUB R2, R2, R0
|
||||
BL memset
|
||||
BL _irq_setup
|
||||
B .
|
||||
|
||||
.globl pivot_stack
|
||||
.type pivot_stack, %function
|
||||
pivot_stack:
|
||||
MOV SP, R0
|
||||
BX LR
|
||||
@@ -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,5 +0,0 @@
|
||||
# hoc-clk
|
||||
|
||||
Overclocking suite for Nintendo Switch.
|
||||
|
||||
Support is only provided for FW 17.0.0+. This MAY work on older firmwares but support is NOT guaranteed
|
||||
Binary file not shown.
@@ -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,404 +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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <switch/types.h>
|
||||
typedef enum
|
||||
{
|
||||
HocClkSocType_Erista = 0, // T210, found in Icosa and Copper
|
||||
HocClkSocType_Mariko, // T214/T210B01, found in Hoag, Iowa, Calcio and Aula
|
||||
// HocClkSocType_Drake, // T239, found in Switch 2. Maybe someday...
|
||||
HocClkSocType_EnumMax
|
||||
} HocClkSocType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HocClkConsoleType_Icosa = 0, // V1
|
||||
HocClkConsoleType_Iowa, // V2
|
||||
HocClkConsoleType_Hoag, // Lite
|
||||
HocClkConsoleType_Aula, // OLED
|
||||
HocClkConsoleType_EnumMax,
|
||||
} HocClkConsoleType;
|
||||
|
||||
typedef enum {
|
||||
HocClkVoltage_SOC = 0, // VDD_SOC rail.
|
||||
HocClkVoltage_EMCVDD2, // DRAM VDD2 rail
|
||||
HocClkVoltage_CPU, // CPU rail
|
||||
HocClkVoltage_GPU, // GPU rail
|
||||
HocClkVoltage_EMCVDDQ, // DRAM VDDQ rail
|
||||
HocClkVoltage_Display, // Display rail
|
||||
HocClkVoltage_Battery, // Battery voltage
|
||||
HocClkVoltage_EnumMax,
|
||||
} HocClkVoltage;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HocClkProfile_Handheld = 0,
|
||||
HocClkProfile_HandheldCharging, // Not a real profile, just a marker
|
||||
HocClkProfile_HandheldChargingUSB,
|
||||
HocClkProfile_HandheldChargingOfficial,
|
||||
HocClkProfile_Docked, // Not shown on Lites
|
||||
HocClkProfile_EnumMax
|
||||
} HocClkProfile;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HocClkModule_CPU = 0,
|
||||
HocClkModule_GPU,
|
||||
HocClkModule_MEM,
|
||||
HocClkModule_Governor,
|
||||
HocClkModule_Display,
|
||||
HocClkModule_EnumMax,
|
||||
} HocClkModule;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HocClkThermalSensor_SOC = 0, // SoC temperature in millicelcius
|
||||
HocClkThermalSensor_PCB, // PCB temperature in millicelcius
|
||||
HocClkThermalSensor_Skin, // "Skin" temperature in millicelcius
|
||||
HocClkThermalSensor_Battery, // Battery temperature in millicelcius
|
||||
HocClkThermalSensor_PMIC, // Always return 50.0C, as thats the only reasonable value the PMIC sensor can generate
|
||||
HocClkThermalSensor_CPU, // CPU temperature in millicelcius
|
||||
HocClkThermalSensor_GPU, // GPU temperature in millicelcius
|
||||
HocClkThermalSensor_MEM, // MEM temperature in millicelcius. Returns the PLLX sensor value on Mariko
|
||||
HocClkThermalSensor_PLLX, // PLLX temperature in millicelcius
|
||||
HocClkThermalSensor_AO, // AOTAG
|
||||
HocClkThermalSensor_BQ24193, // BQ24193 temperature. Refer to BQ24193Temp for returned values
|
||||
HocClkThermalSensor_EnumMax
|
||||
} HocClkThermalSensor;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HocClkPowerSensor_Now = 0,
|
||||
HocClkPowerSensor_Avg,
|
||||
HocClkPowerSensor_EnumMax
|
||||
} HocClkPowerSensor;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HocClkPartLoad_EMC = 0,
|
||||
HocClkPartLoad_EMCCpu,
|
||||
HocClkPartLoad_GPU,
|
||||
HocClkPartLoad_CPUMax,
|
||||
HocClkPartLoad_BAT, // Battery raw charge percentage
|
||||
HocClkPartLoad_FAN,
|
||||
HocClkPartLoad_RamBWAll,
|
||||
HocClkPartLoad_RamBWCpu,
|
||||
HocClkPartLoad_RamBWGpu,
|
||||
HocClkPartLoad_RamBWPeak, // Maximum possible RAM bandwidth
|
||||
HocClkPartLoad_EnumMax
|
||||
} HocClkPartLoad;
|
||||
|
||||
typedef enum {
|
||||
HocClkSpeedo_CPU = 0,
|
||||
HocClkSpeedo_GPU,
|
||||
HocClkSpeedo_SOC,
|
||||
HocClkSpeedo_EnumMax,
|
||||
} HocClkSpeedo;
|
||||
|
||||
typedef enum {
|
||||
GPUUVLevel_HiOPT = 0,
|
||||
GPUUVLevel_HiOPT15,
|
||||
GPUUVLevel_HighUV,
|
||||
GPUUVLevel_EnumMax,
|
||||
} GPUUndervoltLevel;
|
||||
|
||||
enum {
|
||||
DVFSMode_Disabled = 0,
|
||||
DVFSMode_Hijack, // PCV hijack dvfs
|
||||
// DVFSMode_OfficialService,
|
||||
// DVFSMode_Hack,
|
||||
DVFSMode_EnumMax,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GpuSchedulingMode_DoNotOverride = 0,
|
||||
GpuSchedulingMode_Enabled,
|
||||
GpuSchedulingMode_Disabled,
|
||||
GpuSchedulingMode_EnumMax,
|
||||
} GpuSchedulingMode;
|
||||
|
||||
typedef enum {
|
||||
GpuSchedulingOverrideMethod_Ini = 0,
|
||||
GpuSchedulingOverrideMethod_NvService,
|
||||
GpuSchedulingOverrideMethod_EnumMax,
|
||||
} GpuSchedulingOverrideMethod;
|
||||
typedef enum {
|
||||
ComponentGovernor_DoNotOverride = 0,
|
||||
ComponentGovernor_Disabled = 1,
|
||||
ComponentGovernor_Enabled = 2,
|
||||
ComponentGovernor_EnumMax,
|
||||
} ComponentGovernorState;
|
||||
typedef enum {
|
||||
RamDisplayMode_VDD2 = 0,
|
||||
RamDisplayMode_VDDQ,
|
||||
RamDisplayMode_EnumMax,
|
||||
} RamDisplayMode;
|
||||
|
||||
typedef enum {
|
||||
MemoryFrequencyMeasurementMode_PLL = 0,
|
||||
MemoryFrequencyMeasurementMode_Actmon,
|
||||
MemoryFrequencyMeasurementMode_EnumMax,
|
||||
} MemoryFrequencyMeasurementMode;
|
||||
|
||||
typedef enum {
|
||||
RamDisplayUnit_MHz = 0,
|
||||
RamDisplayUnit_MTs,
|
||||
RamDisplayUnit_MHzMTs,
|
||||
RamDisplayUnit_EnumMax,
|
||||
} RamDisplayUnit;
|
||||
typedef enum {
|
||||
BQ24193Temp_Normal = 0,
|
||||
BQ24193Temp_Warm,
|
||||
BQ24193Temp_Hot,
|
||||
BQ24193Temp_Overheat,
|
||||
BQ24193Temp_EnumMax
|
||||
} BQ24193Temp;
|
||||
typedef enum AulaColorMode {
|
||||
AulaDisplayColorMode_DoNotOverride = 0xFF,
|
||||
AulaDisplayColorMode_Saturated = 0x0,
|
||||
AulaDisplayColorMode_Washed = 0x45,
|
||||
AulaDisplayColorMode_Basic = 0x03, // Default
|
||||
AulaDisplayColorMode_PowerReset = 0x20, // Reset to on power on
|
||||
AulaDisplayColorMode_Natural = 0x23,
|
||||
AulaDisplayColorMode_Vivid = 0x65,
|
||||
AulaDisplayColorMode_Night0 = 0x43,
|
||||
AulaDisplayColorMode_Night1 = 0x15,
|
||||
AulaDisplayColorMode_Night2 = 0x35,
|
||||
AulaDisplayColorMode_Night3 = 0x75,
|
||||
} AulaColorMode;
|
||||
|
||||
// typedef enum {
|
||||
// PANEL_JDI_XXX062M = 0x10,
|
||||
// PANEL_JDI_LAM062M109A = 0x0910, // SI.
|
||||
// PANEL_JDI_LPM062M326A = 0x2610, // LTPS.
|
||||
// PANEL_INL_P062CCA_AZ1 = 0x0F20,
|
||||
// PANEL_AUO_A062TAN01 = 0x0F30,
|
||||
// PANEL_INL_2J055IA_27A = 0x1020,
|
||||
// PANEL_AUO_A055TAN01 = 0x1030,
|
||||
// PANEL_SHP_LQ055T1SW10 = 0x1040,
|
||||
// PANEL_SAM_AMS699VC01 = 0x2050,
|
||||
|
||||
// PANEL_RR_SUPER5_OLED_V1 = 0x10E0,
|
||||
// PANEL_RR_SUPER5_OLED_HD_V1 = 0x10E1,
|
||||
// PANEL_RR_SUPER7_IPS_V1 = 0x0FE0,
|
||||
// PANEL_RR_SUPER7_IPS_HD_V1 = 0x0FE1
|
||||
// // Found on 6/2" clones. Unknown markings. Clone of AUO A062TAN01.
|
||||
// // Quality seems JDI like. Has bad low backlight scaling. ID: [83] 94 [0F]. Sometimes reports [30] 94 [0F]. Both IDs have correct CRC16.
|
||||
// PANEL_OEM_CLONE_6_2 = 0x0F83,
|
||||
// // Found on 5.5" clones with AUO A055TAN02 (59.05A30.001) fake markings.
|
||||
// PANEL_OEM_CLONE_5_5 = 0x00B3,
|
||||
// // Found on 5.5" clones with AUO A055TAN02 (59.05A30.001) fake markings.
|
||||
// PANEL_OEM_CLONE = 0x0000
|
||||
// //0x0F40 [40] 94 [0F], 5.5" clone
|
||||
// } HocClkDisplayPanel;
|
||||
|
||||
#define HOCCLK_ENUM_VALID(n, v) ((v) < n##_EnumMax)
|
||||
|
||||
// Packed u32
|
||||
// Bits 0-7 - CPU
|
||||
// Bits 8-15 - GPU
|
||||
// Bits 16-23 - VRR
|
||||
// Bits 24-32 - unused
|
||||
|
||||
inline u32 GovernorStatePack(u8 cpu, u8 gpu, u8 vrr) {
|
||||
return (u32)cpu | ((u32)gpu << 8) | ((u32)vrr << 16);
|
||||
}
|
||||
inline u8 GovernorStateCpu(u32 p) {
|
||||
return (u8)(p & 0xFF);
|
||||
}
|
||||
inline u8 GovernorStateGpu(u32 p) {
|
||||
return (u8)((p >> 8) & 0xFF);
|
||||
}
|
||||
inline u8 GovernorStateVrr(u32 p) {
|
||||
return (u8)((p >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
static inline const char* hocclkFormatModule(HocClkModule module, bool pretty)
|
||||
{
|
||||
switch(module)
|
||||
{
|
||||
case HocClkModule_CPU:
|
||||
return pretty ? "CPU" : "cpu";
|
||||
case HocClkModule_GPU:
|
||||
return pretty ? "GPU" : "gpu";
|
||||
case HocClkModule_MEM:
|
||||
return pretty ? "Memory" : "mem";
|
||||
case HocClkModule_Display:
|
||||
return pretty ? "Display" : "display";
|
||||
case HocClkModule_Governor:
|
||||
return pretty ? "Governor" : "governor";
|
||||
default:
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* hocclkFormatThermalSensor(HocClkThermalSensor thermSensor, bool pretty)
|
||||
{
|
||||
switch(thermSensor) {
|
||||
case HocClkThermalSensor_SOC:
|
||||
return pretty ? "SOC" : "soc";
|
||||
case HocClkThermalSensor_PCB:
|
||||
return pretty ? "PCB" : "pcb";
|
||||
case HocClkThermalSensor_Skin:
|
||||
return pretty ? "Skin" : "skin";
|
||||
case HocClkThermalSensor_Battery:
|
||||
return pretty ? "BAT" : "battery";
|
||||
case HocClkThermalSensor_PMIC:
|
||||
return pretty ? "PMIC" : "pmic";
|
||||
case HocClkThermalSensor_CPU:
|
||||
return pretty ? "CPU" : "cpu";
|
||||
case HocClkThermalSensor_GPU:
|
||||
return pretty ? "GPU" : "gpu";
|
||||
case HocClkThermalSensor_MEM:
|
||||
return pretty ? "MEM" : "mem";
|
||||
case HocClkThermalSensor_PLLX:
|
||||
return pretty ? "PLLX" : "pllx";
|
||||
case HocClkThermalSensor_AO:
|
||||
return pretty ? "AO" : "ao";
|
||||
case HocClkThermalSensor_BQ24193:
|
||||
return pretty ? "BQ24193" : "bq24193";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* hocclkFormatPowerSensor(HocClkPowerSensor powSensor, bool pretty)
|
||||
{
|
||||
switch(powSensor)
|
||||
{
|
||||
case HocClkPowerSensor_Now:
|
||||
return pretty ? "Now" : "now";
|
||||
case HocClkPowerSensor_Avg:
|
||||
return pretty ? "Avg" : "avg";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* hocclkFormatProfile(HocClkProfile profile, bool pretty)
|
||||
{
|
||||
switch(profile)
|
||||
{
|
||||
case HocClkProfile_Docked:
|
||||
return pretty ? "Docked" : "docked";
|
||||
case HocClkProfile_Handheld:
|
||||
return pretty ? "Handheld" : "handheld";
|
||||
case HocClkProfile_HandheldCharging:
|
||||
return pretty ? "Charging" : "handheld_charging";
|
||||
case HocClkProfile_HandheldChargingUSB:
|
||||
return pretty ? "USB Charger" : "handheld_charging_usb";
|
||||
case HocClkProfile_HandheldChargingOfficial:
|
||||
return pretty ? "PD Charger" : "handheld_charging_official";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline const char* hocClkFormatVoltage(HocClkVoltage voltage, bool pretty)
|
||||
{
|
||||
switch(voltage)
|
||||
{
|
||||
case HocClkVoltage_CPU:
|
||||
return pretty ? "CPU" : "cpu";
|
||||
case HocClkVoltage_GPU:
|
||||
return pretty ? "GPU" : "gpu";
|
||||
case HocClkVoltage_EMCVDD2:
|
||||
return pretty ? "VDD2" : "vdd2";
|
||||
case HocClkVoltage_EMCVDDQ:
|
||||
return pretty ? "VDDQ" : "vddq";
|
||||
case HocClkVoltage_SOC:
|
||||
return pretty ? "SOC" : "soc";
|
||||
case HocClkVoltage_Display:
|
||||
return pretty ? "Display" : "display";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char* hocClkFormatConsoleType(HocClkConsoleType consoleType, bool pretty)
|
||||
{
|
||||
switch(consoleType)
|
||||
{
|
||||
case HocClkConsoleType_Icosa:
|
||||
return pretty ? "Icosa (V1)" : "icosa";
|
||||
case HocClkConsoleType_Iowa:
|
||||
return pretty ? "Iowa (V2)" : "iowa";
|
||||
case HocClkConsoleType_Hoag:
|
||||
return pretty ? "Hoag (Lite)" : "hoag";
|
||||
case HocClkConsoleType_Aula:
|
||||
return pretty ? "Aula (OLED)" : "aula";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// static inline const char* hocClkFormatPanel(HocClkDisplayPanel panel, bool pretty)
|
||||
// {
|
||||
// switch(panel)
|
||||
// {
|
||||
// case PANEL_JDI_XXX062M:
|
||||
// return pretty ? "JDI XXX062M" : "jdi_xxx062m";
|
||||
// case PANEL_JDI_LAM062M109A:
|
||||
// return pretty ? "JDI LAM062M109A" : "jdi_lam062m109a";
|
||||
// case PANEL_JDI_LPM062M326A:
|
||||
// return pretty ? "JDI LPM062M326A" : "jdi_lpm062m326a";
|
||||
// case PANEL_INL_P062CCA_AZ1:
|
||||
// return pretty ? "INL P062CCA-AZ1" : "inl_p062cca_az1";
|
||||
// case PANEL_AUO_A062TAN01:
|
||||
// return pretty ? "AUO A062TAN01" : "auo_a062tan01";
|
||||
// case PANEL_INL_2J055IA_27A:
|
||||
// return pretty ? "INL 2J055IA-27A" : "inl_2j055ia_27a";
|
||||
// case PANEL_AUO_A055TAN01:
|
||||
// return pretty ? "AUO A055TAN01" : "auo_a055tan01";
|
||||
// case PANEL_SHP_LQ055T1SW10:
|
||||
// return pretty ? "SHP LQ055T1SW10" : "shp_lq055t1sw10";
|
||||
// case PANEL_SAM_AMS699VC01:
|
||||
// return pretty ? "SAM AMS699VC01" : "sam_ams699vc01";
|
||||
// case PANEL_RR_SUPER5_OLED_V1:
|
||||
// return pretty ? "SUPER5 OLED" : "rr_super5_oled_v1";
|
||||
// case PANEL_RR_SUPER5_OLED_HD_V1:
|
||||
// return pretty ? "SUPER5 OLED HD" : "rr_super5_oled_hd_v1";
|
||||
// case PANEL_RR_SUPER7_IPS_V1:
|
||||
// return pretty ? "SUPER7 IPS" : "rr_super7_ips_v1";
|
||||
// case PANEL_RR_SUPER7_IPS_HD_V1:
|
||||
// return pretty ? "RR Super7 IPS HD V1" : "rr_super7_ips_hd_v1";
|
||||
// case PANEL_OEM_CLONE_6_2:
|
||||
// return pretty ? "OEM Clone 6.2" : "oem_clone_6_2";
|
||||
// case PANEL_OEM_CLONE_5_5:
|
||||
// return pretty ? "OEM Clone 5.5" : "oem_clone_5_5";
|
||||
// case PANEL_OEM_CLONE:
|
||||
// return pretty ? "OEM Clone" : "oem_clone";
|
||||
// default:
|
||||
// return "unknown";
|
||||
// }
|
||||
// }
|
||||
@@ -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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
* "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 <stdint.h>
|
||||
#include "board.h"
|
||||
|
||||
typedef struct {
|
||||
|
||||
/*
|
||||
* This "stable struct" must never be modified. It provides a fixed memory layout so external clients can safely read the expected fields even
|
||||
* if HocClkContext changes in newer versions and the client is not recompiled.
|
||||
*/
|
||||
struct {
|
||||
#define HocClkModuleStable_EnumMax 5
|
||||
#define HocClkThermalSensorStable_EnumMax 11
|
||||
#define HocClkPowerSensorStable_EnumMax 2
|
||||
#define HocClkPartLoadStable_EnumMax 10
|
||||
#define HocClkVoltageStable_EnumMax 7
|
||||
|
||||
u32 freqs[HocClkModuleStable_EnumMax];
|
||||
u32 realFreqs[HocClkModuleStable_EnumMax];
|
||||
u32 overrideFreqs[HocClkModuleStable_EnumMax];
|
||||
s32 temps[HocClkThermalSensorStable_EnumMax];
|
||||
s32 power[HocClkPowerSensorStable_EnumMax];
|
||||
u32 partLoad[HocClkPartLoadStable_EnumMax];
|
||||
u32 voltages[HocClkVoltageStable_EnumMax];
|
||||
} stable;
|
||||
|
||||
uint64_t applicationId;
|
||||
HocClkProfile profile;
|
||||
uint32_t freqs[HocClkModule_EnumMax];
|
||||
uint32_t realFreqs[HocClkModule_EnumMax];
|
||||
uint32_t overrideFreqs[HocClkModule_EnumMax];
|
||||
int32_t temps[HocClkThermalSensor_EnumMax];
|
||||
int32_t power[HocClkPowerSensor_EnumMax];
|
||||
uint32_t partLoad[HocClkPartLoad_EnumMax];
|
||||
uint32_t voltages[HocClkVoltage_EnumMax];
|
||||
u16 speedos[HocClkSpeedo_EnumMax];
|
||||
u16 iddq[HocClkSpeedo_EnumMax];
|
||||
s16 waferX;
|
||||
s16 waferY;
|
||||
|
||||
// Misc stuff
|
||||
GpuSchedulingMode gpuSchedulingMode;
|
||||
bool isSysDockInstalled;
|
||||
bool isSaltyNXInstalled;
|
||||
bool isUsingRetroSuper;
|
||||
u8 maxDisplayFreq;
|
||||
u8 dramID;
|
||||
bool isDram8GB;
|
||||
HocClkConsoleType consoleType;
|
||||
|
||||
// FPS / Resolution
|
||||
u8 fps;
|
||||
u16 resolutionHeight;
|
||||
u8 custRev;
|
||||
u16 kipVersion;
|
||||
|
||||
// Reserved for future use
|
||||
u8 reserved[0x35B];
|
||||
} HocClkContext;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union {
|
||||
uint32_t mhz[+HocClkProfile_EnumMax * +HocClkModule_EnumMax];
|
||||
uint32_t mhzMap[+HocClkProfile_EnumMax][+HocClkModule_EnumMax];
|
||||
};
|
||||
} HocClkTitleProfileList;
|
||||
|
||||
#define HOCCLK_FREQ_LIST_MAX 48
|
||||
|
||||
#define HOCCLK_GLOBAL_PROFILE_TID 0xA111111111111111
|
||||
|
||||
static_assert(sizeof(HocClkContext) == 0x500);
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#define R_UNLESS(rc) \
|
||||
do { \
|
||||
if (R_FAILED(rc)) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* TODO: Add more Result macros. */
|
||||
@@ -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,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 de sys-dock:",
|
||||
"SaltyNX status:": "Estado de SaltyNX:",
|
||||
"RR Display status:": "Estado de pantalla RR:",
|
||||
"Wafer Position:": "Posición del wafer:",
|
||||
"Credits": "Créditos",
|
||||
"Developers": "Desarrolladores",
|
||||
"Contributors": "Colaboradores",
|
||||
"Testers": "Testers",
|
||||
"Special Thanks": "Agradecimientos especiales",
|
||||
"Unknown": "Desconocido",
|
||||
"Installed": "Instalado",
|
||||
"Not Installed": "No instalado",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "LICENCIA BEER-WARE",
|
||||
"Default": "Predeterminado",
|
||||
"Do Not Override": "No sobrescribir",
|
||||
"Disabled": "Desactivado",
|
||||
"Enabled": "Activado",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Restablecer",
|
||||
"Display": "Pantalla",
|
||||
"Application changed\\n\\n": "Aplicación cambiada\\n\\n",
|
||||
"The running application changed\\n\\n": "La aplicación en ejecución ha cambiado\\n\\n",
|
||||
"while editing was going on.": "mientras se estaba editando.",
|
||||
"Board": "Placa",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "No se pudo conectar al sysmodule hoc-clk.\\n\\n",
|
||||
"Please make sure everything is\\n\\n": "Asegúrate de que todo esté\\n\\n",
|
||||
"correctly installed and enabled.": "correctamente instalado y activado.",
|
||||
"Fatal error": "Error fatal",
|
||||
"Temporary Overrides ": "Ajustes temporales",
|
||||
"Sleep Mode": "Modo reposo",
|
||||
"Stock": "Valores de fábrica",
|
||||
"Dev OC": "OC de desarrollo",
|
||||
"Boost Mode": "Modo boost",
|
||||
"Safe Max": "Máximo seguro",
|
||||
"Unsafe Max": "Máximo no seguro",
|
||||
"Absolute Max": "Máximo absoluto",
|
||||
"Handheld Safe Max": "Máximo seguro en portátil",
|
||||
"Enable": "Activar",
|
||||
"Edit App Profile": "Editar perfil de aplicación",
|
||||
"Edit Global Profile": "Editar perfil global",
|
||||
"Temporary Overrides": "Ajustes temporales",
|
||||
"Settings": "Configuración",
|
||||
"About": "Acerca de",
|
||||
"Compiling with minimal features": "Compilado con funciones mínimas",
|
||||
"General Settings": "Configuración general",
|
||||
"Governor Settings": "Configuración del governor",
|
||||
"Safety Settings": "Configuración de seguridad",
|
||||
"Save KIP Settings": "Guardar configuración KIP",
|
||||
"RAM Settings": "Configuración de RAM",
|
||||
"CPU Settings": "Configuración de CPU",
|
||||
"GPU Settings": "Configuración de GPU",
|
||||
"Display Settings": "Configuración de pantalla",
|
||||
"Experimental": "Experimental",
|
||||
"GPU Scheduling Override Method": "Método de sobrescritura del scheduling de GPU",
|
||||
"can be dangerous and may cause": "puede ser peligroso y causar",
|
||||
"damage to your battery or charger!": "daños a la batería o al cargador.",
|
||||
"Charge Current Override": "Sobrescritura 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 governor de CPU",
|
||||
"refresh rates may cause stress": "las tasas de refresco pueden causar estrés",
|
||||
"or damage to your display! ": "o dañar la pantalla.",
|
||||
"Proceed at your own risk!": "¡Úsalo bajo tu propio riesgo!",
|
||||
"Max Handheld Display": "Frecuencia máxima de pantalla en portátil",
|
||||
"Display Clock": "Frecuencia de pantalla",
|
||||
"Official Rating": "Valor oficial",
|
||||
"TDP Threshold": "Umbral de TDP",
|
||||
"Power": "Potencia",
|
||||
"Thermal Throttle Limit": "Límite de thermal throttling",
|
||||
"HP Mode": "Modo alto rendimiento",
|
||||
"Default (Mariko)": "Predeterminado (Mariko)",
|
||||
"Default (Erista)": "Predeterminado (Erista)",
|
||||
"Rating": "Valor",
|
||||
"Safe Max (Mariko)": "Máximo seguro (Mariko)",
|
||||
"Safe Max (Erista)": "Máximo seguro (Erista)",
|
||||
"RAM VDD2 Voltage": "Voltaje VDD2 de RAM",
|
||||
"Voltage": "Voltaje",
|
||||
"RAM VDDQ Voltage": "Voltaje VDDQ de RAM",
|
||||
"RAM Frequency Editor": "Editor de frecuencia de RAM",
|
||||
"JEDEC.": "JEDEC",
|
||||
"High speedo needed!": "¡Se necesita alto speedo!",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333MHz (requiere Speedo/PLL extremo)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366MHz (requiere Speedo/PLL extremo)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400MHz (requiere Speedo/PLL extremo)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (requiere Speedo/PLL muy alto)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (requiere Speedo/PLL muy alto)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz (requiere Speedo/PLL muy alto)",
|
||||
"Ram Max Clock": "Frecuencia máxima de RAM",
|
||||
"RAM Latency Editor": "Editor de latencias de RAM",
|
||||
"RAM Timing Reductions": "Reducción de timings de RAM",
|
||||
"Memory Timings": "Timings de memoria",
|
||||
"Advanced": "Avanzado",
|
||||
"t6 tRTW Fine Tune": "Ajuste fino t6 tRTW",
|
||||
"tRTW Fine Tune": "Ajuste fino tRTW",
|
||||
"t7 tWTR Fine Tune": "Ajuste fino t7 tWTR",
|
||||
"tWTR Fine Tune": "Ajuste fino tWTR",
|
||||
"Memory Latencies": "Latencias de memoria",
|
||||
"Read Latency": "Latencia de lectura",
|
||||
"Write Latency": "Latencia de escritura",
|
||||
"CPU Boost Clock": "Frecuencia boost de CPU",
|
||||
"CPU UV": "Undervolt de CPU",
|
||||
"CPU Unlock": "Desbloqueo de CPU",
|
||||
"CPU VMIN": "VMIN de CPU",
|
||||
"CPU Max Voltage": "Voltaje máximo de CPU",
|
||||
"CPU Max Clock": "Frecuencia máxima de CPU",
|
||||
"Extreme UV Table": "Tabla de undervolt extrema",
|
||||
"CPU UV Table": "Tabla de undervolt de CPU",
|
||||
"CPU Low UV": "Undervolt bajo de CPU",
|
||||
"CPU High UV": "Undervolt alto de CPU",
|
||||
"CPU Low VMIN": "VMIN bajo de CPU",
|
||||
"CPU High VMIN": "VMIN alto de CPU",
|
||||
"No Undervolt": "Sin undervolt",
|
||||
"SLT Table": "Tabla SLT",
|
||||
"HiOPT Table": "Tabla HiOPT",
|
||||
"GPU Undervolt Table": "Tabla de undervolt de GPU",
|
||||
"GPU Minimum Voltage": "Voltaje mínimo de GPU",
|
||||
"Calculate GPU Vmin": "Calcular Vmin de GPU",
|
||||
"GPU VMIN": "VMIN de GPU",
|
||||
"GPU Maximum Voltage": "Voltaje máximo de GPU",
|
||||
"GPU Voltage Offset": "Offset de voltaje de GPU",
|
||||
"Do not override": "No sobrescribir",
|
||||
"Enabled (Default)": "Activado (predeterminado)",
|
||||
"96.6% limit": "Límite 96,6%",
|
||||
"99.7% limit": "Límite 99,7%",
|
||||
"GPU Scheduling Override": "Sobrescritura de scheduling de GPU",
|
||||
"Official Service": "Servicio oficial",
|
||||
"GPU DVFS Mode": "Modo DVFS de GPU",
|
||||
"GPU DVFS Offset": "Offset 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 undervolt, 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 undervolt y 960MHz en",
|
||||
"SLT or HiOPT can cause ": "SLT o HiOPT pueden causar "
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Information": "Informations",
|
||||
"IDDQ:": "IDDQ :",
|
||||
"Module: ": "Module :",
|
||||
"sys-dock status:": "Statut de sys-dock :",
|
||||
"SaltyNX status:": "Statut de SaltyNX :",
|
||||
"RR Display status:": "Statut de l'affichage RR :",
|
||||
"Wafer Position:": "Position du wafer :",
|
||||
"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 BEER-WARE",
|
||||
"Default": "Par défaut",
|
||||
"Do Not Override": "Ne pas remplacer",
|
||||
"Disabled": "Désactivé",
|
||||
"Enabled": "Activé",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Réinitialiser",
|
||||
"Display": "Écran",
|
||||
"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 la modification.",
|
||||
"Board": "Carte",
|
||||
"%u.%u%u mV": "%u.%u%u mV",
|
||||
"Could not connect to hoc-clk sysmodule.\\n\\n": "Impossible de se connecter au sysmodule 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 ": "Forçages temporaires ",
|
||||
"Sleep Mode": "Mode veille",
|
||||
"Stock": "D'origine",
|
||||
"Dev OC": "OC Développeur",
|
||||
"Boost Mode": "Mode Boost",
|
||||
"Safe Max": "Max sûr",
|
||||
"Unsafe Max": "Max non sûr",
|
||||
"Absolute Max": "Max absolu",
|
||||
"Handheld Safe Max": "Max sûr (mode portable)",
|
||||
"Enable": "Activer",
|
||||
"Edit App Profile": "Modifier le profil de l'app",
|
||||
"Edit Global Profile": "Modifier le profil global",
|
||||
"Temporary Overrides": "Forçages temporaires",
|
||||
"Settings": "Paramètres",
|
||||
"About": "À propos",
|
||||
"Compiling with minimal features": "Compilation avec 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 RAM",
|
||||
"CPU Settings": "Paramètres CPU",
|
||||
"GPU Settings": "Paramètres GPU",
|
||||
"Display Settings": "Paramètres d'affichage",
|
||||
"Experimental": "Expérimental",
|
||||
"GPU Scheduling Override Method": "Méthode de Forçage de l'ordonnancement GPU",
|
||||
"can be dangerous and may cause": "peut être dangereux et causer des",
|
||||
"damage to your battery or charger!": "dommages à votre batterie ou chargeur !",
|
||||
"Charge Current Override": "Forçage du courant de charge",
|
||||
"RAM Voltage Display Mode": "Mode d'affichage de la tension RAM",
|
||||
"Polling Interval": "Intervalle d'interrogation",
|
||||
"CPU Governor Minimum Frequency": "Fréquence minimale du gouverneur CPU",
|
||||
"refresh rates may cause stress": "les taux de rafraîchissement peuvent stresser",
|
||||
"or damage to your display! ": "ou endommager votre écran !",
|
||||
"Proceed at your own risk!": "À utiliser à vos propres risques !",
|
||||
"Max Handheld Display": "Affichage portable max",
|
||||
"Display Clock": "Fréquence d'affichage",
|
||||
"Official Rating": "Classement officiel",
|
||||
"TDP Threshold": "Seuil TDP",
|
||||
"Power": "Alimentation",
|
||||
"Thermal Throttle Limit": "Limite d'étranglement thermique",
|
||||
"HP Mode": "Mode HP",
|
||||
"Default (Mariko)": "Par défaut (Mariko)",
|
||||
"Default (Erista)": "Par défaut (Erista)",
|
||||
"Rating": "Évaluation",
|
||||
"Safe Max (Mariko)": "Max sûr (Mariko)",
|
||||
"Safe Max (Erista)": "Max sûr (Erista)",
|
||||
"RAM VDD2 Voltage": "Tension RAM VDD2",
|
||||
"Voltage": "Tension",
|
||||
"RAM VDDQ Voltage": "Tension RAM VDDQ",
|
||||
"RAM Frequency Editor": "Éditeur de fréquence RAM",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Speedo élevé requis !",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (nécessite Speedo/PLL extrême)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (nécessite Speedo/PLL extrême)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (nécessite Speedo/PLL extrême)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 MHz (nécessite Speedo/PLL ridicule)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (nécessite Speedo/PLL ridicule)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (nécessite Speedo/PLL ridicule)",
|
||||
"Ram Max Clock": "Fréquence RAM max",
|
||||
"RAM Latency Editor": "Éditeur de latence RAM",
|
||||
"RAM Timing Reductions": "Réductions des timings RAM",
|
||||
"Memory Timings": "Timings mémoire",
|
||||
"Advanced": "Avancé",
|
||||
"t6 tRTW Fine Tune": "Ajustement précis t6 tRTW",
|
||||
"tRTW Fine Tune": "Ajustement précis tRTW",
|
||||
"t7 tWTR Fine Tune": "Ajustement précis t7 tWTR",
|
||||
"tWTR Fine Tune": "Ajustement précis tWTR",
|
||||
"Memory Latencies": "Latences mémoire",
|
||||
"Read Latency": "Latence de lecture",
|
||||
"Write Latency": "Latence d'écriture",
|
||||
"CPU Boost Clock": "Fréquence Boost CPU",
|
||||
"CPU UV": "UV CPU",
|
||||
"CPU Unlock": "Déverrouillage CPU",
|
||||
"CPU VMIN": "CPU VMIN",
|
||||
"CPU Max Voltage": "Tension CPU max",
|
||||
"CPU Max Clock": "Fréquence CPU max",
|
||||
"Extreme UV Table": "Table d'UV extrême",
|
||||
"CPU UV Table": "Table d'UV CPU",
|
||||
"CPU Low UV": "UV CPU faible",
|
||||
"CPU High UV": "UV CPU élevé",
|
||||
"CPU Low VMIN": "VMIN CPU faible",
|
||||
"CPU High VMIN": "VMIN CPU élevé",
|
||||
"No Undervolt": "Aucun Undervolt",
|
||||
"SLT Table": "Table SLT",
|
||||
"HiOPT Table": "Table HiOPT",
|
||||
"GPU Undervolt Table": "Table d'undervolt GPU",
|
||||
"GPU Minimum Voltage": "Tension GPU minimale",
|
||||
"Calculate GPU Vmin": "Calculer Vmin GPU",
|
||||
"GPU VMIN": "GPU VMIN",
|
||||
"GPU Maximum Voltage": "Tension GPU maximale",
|
||||
"GPU Voltage Offset": "Offset de tension GPU",
|
||||
"Do not override": "Ne pas remplacer",
|
||||
"Enabled (Default)": "Activé (par défaut)",
|
||||
"96.6% limit": "Limite de 96,6 %",
|
||||
"99.7% limit": "Limite de 99,7 %",
|
||||
"GPU Scheduling Override": "Forçage de l'ordonnancement GPU",
|
||||
"Official Service": "Service officiel",
|
||||
"GPU DVFS Mode": "Mode GPU DVFS",
|
||||
"GPU DVFS Offset": "Offset GPU DVFS",
|
||||
"GPU Voltage Table": "Table de tension GPU",
|
||||
"GPU Custom Table (mV)": "Table de GPU personnalisée (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 causer des",
|
||||
"permanent damage to your Switch!": "dommages permanents à votre Switch !",
|
||||
"921MHz without UV and 960MHz on": "921 MHz sans UV et 960 MHz sur",
|
||||
"SLT or HiOPT can cause ": "SLT ou HiOPT peuvent causer des"
|
||||
}
|
||||
@@ -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,179 +0,0 @@
|
||||
{
|
||||
"Information": "Информация",
|
||||
"IDDQ:": "IDDQ:",
|
||||
"Module: ": "Module:",
|
||||
"sys-dock status:": "Статус sys-dock:",
|
||||
"SaltyNX status:": "Статус SaltyNX:",
|
||||
"RR Display status:": "Статус RR Display:",
|
||||
"Wafer Position:": "Wafer Position:",
|
||||
"Credits": "Благодарности",
|
||||
"Developers": "Разработчики",
|
||||
"Contributors": "Внесли вклад",
|
||||
"Testers": "Тестеры",
|
||||
"Special Thanks": "Особая благодарность",
|
||||
"Unknown": "Неизвестно",
|
||||
"Installed": "Установлено",
|
||||
"Not Installed": "Не установлено",
|
||||
"X: %u Y: %u": "X: %u Y: %u",
|
||||
"THE BEER-WARE LICENSE": "BEER-WARE LICENSE",
|
||||
"Default": "По умолчанию",
|
||||
"Do Not Override": "Не менять",
|
||||
"Disabled": "Отключено",
|
||||
"Enabled": "Включено",
|
||||
"Auto": "Авто",
|
||||
" \\ue0e3 Reset": "\\ue0e3 Сброс",
|
||||
"Display": "Дисплей",
|
||||
"Application changed\\n\\n": "Приложение изменено\\n\\n",
|
||||
"The running application changed\\n\\n": "Запущенное приложение изменилось\\n\\n",
|
||||
"while editing was going on.": "пока шло редактирование.",
|
||||
"Board": "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": "Разгон dev-кита",
|
||||
"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": "Собрано с урезанием функций",
|
||||
"\uE150 Settings marked in blue": "Настройки помеченные синим",
|
||||
"don't require a reboot to apply!": "Синие настройки применяются сразу!",
|
||||
"General Settings": "Основные настройки",
|
||||
"Governor Settings": "Настройки говернора",
|
||||
"Safety Settings": "Настройки безопасности",
|
||||
"Save KIP Settings": "Сохранить настройки KIP",
|
||||
"RAM Settings": "Настройки RAM",
|
||||
"CPU Settings": "Настройки CPU",
|
||||
"GPU Settings": "Настройки GPU",
|
||||
"Display Settings": "Настройки дисплея",
|
||||
"Experimental": "Экспериментальный",
|
||||
"Enable Experimental Settings": "Экспериментальные настройки",
|
||||
"\uE150 Experimental Settings are incomplete ": "Экспериментальные настройки не закончены",
|
||||
"and may not work correctly or at all!": "Экспериментальные настройки не",
|
||||
"Here be dragons!": "закончены и могут не работать!",
|
||||
"Experimental Settings": "Экспериментальные",
|
||||
"Live CPU Undervolt": "Мгновенный андервольт CPU",
|
||||
"GPU Scheduling Override Method": "Метод перезаписи планировщика GPU",
|
||||
"Memory Frequency Measurement Mode": "Режим измерения частоты RAM",
|
||||
"\uE150 Overriding the charge current": "Перезапись зарядного тока может",
|
||||
"can be dangerous and may cause": "Перезапись зарядного тока может",
|
||||
"damage to your battery or charger!": "повреждить аккумулятор или зарядку!",
|
||||
"Charge Current Override": "Перезапись зарядного тока",
|
||||
"RAM Voltage Display Mode": "Показ вольтажа RAM",
|
||||
"RAM Display Unit": "Показ единицы измерения RAM",
|
||||
"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 Hz": "Макс. в портативе",
|
||||
"Display Clock": "Частота экрана",
|
||||
"Official Rating": "Официальный рейтинг",
|
||||
"TDP Threshold": "Порог TDP",
|
||||
"Power": "Мощность",
|
||||
"Thermal Throttle Limit": "Предел троттлинга",
|
||||
"HP Mode": "Режим HP",
|
||||
"Default (Mariko)": "По умолчанию (M)",
|
||||
"Default (Erista)": "По умолчанию (E)",
|
||||
"Rating": "Рейтинг",
|
||||
"Safe Max (Mariko)": "Сейф Макс (M)",
|
||||
"Safe Max (Erista)": "Сейф Макс (E)",
|
||||
"RAM VDD2 Voltage": "Вольтаж VDD2",
|
||||
"Voltage": "Вольтаж",
|
||||
"RAM VDDQ Voltage": "Вольтаж VDDQ",
|
||||
"Step Mode": "Частотный шаг",
|
||||
"RAM Frequency Editor": "Редактор частоты",
|
||||
"JEDEC.": "JEDEC.",
|
||||
"High speedo needed!": "Для высоких speedo",
|
||||
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (нужны невероятные speedo/PLL)",
|
||||
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (нужны невероятные speedo/PLL)",
|
||||
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (нужны невероятные speedo/PLL)",
|
||||
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 MHz (нужны безумные speedo/PLL)",
|
||||
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (нужны безумные speedo/PLL)",
|
||||
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (нужны безумные speedo/PLL)",
|
||||
"Ram Max Clock": "Макс. частота",
|
||||
"RAM Latency Editor": "Редактор задержек",
|
||||
"1333 Latency Max": "1333 задержка",
|
||||
"1600 Latency Max": "1600 задержка",
|
||||
"1866 Latency Max": "1866 задержка",
|
||||
"2133 Latency Max": "2133 задержка",
|
||||
"RAM Timing Reductions": "Настройка таймингов",
|
||||
"Memory Timings": "Тайминги RAM",
|
||||
"RAM-Timing tBreak": "Разбитие таблицы таймингов",
|
||||
"Memory": "RAM",
|
||||
"mem": "RAM",
|
||||
"MEM": "RAM",
|
||||
"Profile": "Профиль",
|
||||
"Governor": "Говернор",
|
||||
"Advanced": "Расширенные",
|
||||
"Docked": "В доке",
|
||||
"Handheld": "Портатив",
|
||||
"Charging": "На зарядке",
|
||||
"USB Charger": "USB Зарядка",
|
||||
"PD Charger": "PD Зарядка",
|
||||
"Handheld TDP": "TPD в портативе",
|
||||
"Thermal Throttle": "Троттлинг",
|
||||
"Uncapped Clocks": "Максимальные частоты",
|
||||
"SoC DVB Shift": "SoC DVB сдвиг",
|
||||
"SoC Max Volt": "Макс. вольт SoC",
|
||||
"Overwrite Boost Mode": "Перезапись буста",
|
||||
"Display Refresh Rate Changing": "Изменение частоты экрана",
|
||||
"Low t6 tRTW": "Нижний t6 tRTW",
|
||||
"Low t7 tWTR": "Нижний t7 tWTR",
|
||||
"1333WL t2 RP Cap": "Предел 1333WL t2 RP",
|
||||
"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",
|
||||
"CPU Unlock": "Разблокировка CPU",
|
||||
"CPU VMIN": "Мин. вольтаж",
|
||||
"CPU Max Voltage": "Макс. вольтаж",
|
||||
"CPU Max Clock": "Макс. частота",
|
||||
"Extreme UV Table": "Экстримальная",
|
||||
"CPU UV Table": "Таблица андервольта",
|
||||
"CPU Low UV": "Андервольт нижних частот",
|
||||
"CPU High UV": "Андервольт верхних частот",
|
||||
"CPU Low VMIN": "Мин. вольт. нижних частот",
|
||||
"CPU High VMIN": "Мин. вольт. верхних частот",
|
||||
"No Undervolt": "Без андервольта",
|
||||
"SLT Table": "Таблица SLT",
|
||||
"HiOPT Table": "Таблица HiOPT",
|
||||
"GPU Undervolt Table": "Таблица андервольта",
|
||||
"GPU Minimum Voltage": "Мин. вольтаж",
|
||||
"Calculate GPU 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)": "Ручная таблица (мВ)",
|
||||
"\uE150 Setting GPU Clocks past": "\uE150 Установка частот GPU выше",
|
||||
"1228MHz without a proper undervolt": "Установка частот GPU выше 1228 МГц",
|
||||
"can cause degradation or damage": "без хорошего андервольта может",
|
||||
"to your console!": "повредить вашу консоль!"
|
||||
}
|
||||
@@ -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 3e54f2a1ad
@@ -1,97 +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 <cstdio>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <hocclk/board.h>
|
||||
|
||||
#define FREQ_DEFAULT_TEXT "Do not override"
|
||||
|
||||
static inline std::string formatListFreqMHz(std::uint32_t mhz)
|
||||
{
|
||||
if(mhz == 0)
|
||||
{
|
||||
return FREQ_DEFAULT_TEXT;
|
||||
}
|
||||
|
||||
char buf[10];
|
||||
return std::string(buf, snprintf(buf, sizeof(buf), "%u MHz", mhz));
|
||||
}
|
||||
|
||||
static inline std::string formatListFreqHz(uint32_t hz) { return formatListFreqMHz(hz / 1000000); }
|
||||
|
||||
static inline std::string formatListFreqMem(uint32_t mhz, RamDisplayUnit unit)
|
||||
{
|
||||
if(mhz == 0)
|
||||
return FREQ_DEFAULT_TEXT;
|
||||
|
||||
uint32_t mts = mhz * 2;
|
||||
char buf[24];
|
||||
switch(unit)
|
||||
{
|
||||
case RamDisplayUnit_MHz:
|
||||
snprintf(buf, sizeof(buf), "%u MHz", mhz);
|
||||
break;
|
||||
case RamDisplayUnit_MHzMTs:
|
||||
snprintf(buf, sizeof(buf), "%u MHz (%u MT/s)", mhz, mts);
|
||||
break;
|
||||
case RamDisplayUnit_MTs:
|
||||
default:
|
||||
snprintf(buf, sizeof(buf), "%u MT/s", mts);
|
||||
break;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline std::string formatListFreqHzMem(uint32_t hz, RamDisplayUnit unit)
|
||||
{
|
||||
return formatListFreqMem(hz / 1000000, unit);
|
||||
}
|
||||
|
||||
static inline std::string formatMemClockKhzLabel(uint32_t khz, RamDisplayUnit unit)
|
||||
{
|
||||
uint32_t mhz = khz / 1000;
|
||||
uint32_t mts = khz / 500;
|
||||
char buf[32];
|
||||
switch(unit)
|
||||
{
|
||||
case RamDisplayUnit_MHz:
|
||||
snprintf(buf, sizeof(buf), "%u MHz", mhz);
|
||||
break;
|
||||
case RamDisplayUnit_MHzMTs:
|
||||
snprintf(buf, sizeof(buf), "%u MHz (%u MT/s)", mhz, mts);
|
||||
break;
|
||||
case RamDisplayUnit_MTs:
|
||||
default:
|
||||
snprintf(buf, sizeof(buf), "%u MT/s", mts);
|
||||
break;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
@@ -1,450 +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 "about_gui.h"
|
||||
#include "../format.h"
|
||||
#include <tesla.hpp>
|
||||
#include <string>
|
||||
#include "cat.h"
|
||||
#include "ult_ext.h"
|
||||
|
||||
// tsl::elm::ListItem* custRevItem = NULL;
|
||||
tsl::elm::ListItem* kipVersionItem = NULL;
|
||||
tsl::elm::ListItem* SpeedoItem = NULL;
|
||||
tsl::elm::ListItem* IddqItem = NULL;
|
||||
tsl::elm::ListItem* DramModule = NULL;
|
||||
tsl::elm::ListItem* sysdockStatusItem = NULL;
|
||||
tsl::elm::ListItem* saltyNXStatusItem = NULL;
|
||||
tsl::elm::ListItem* RETROStatusItem = NULL;
|
||||
tsl::elm::ListItem* waferCordsItem = NULL;
|
||||
tsl::elm::ListItem* ramVoltItem = NULL;
|
||||
tsl::elm::ListItem* eristaPLLXItem = NULL;
|
||||
tsl::elm::ListItem* dispVoltItem = NULL;
|
||||
tsl::elm::ListItem* ramBWItemAll = NULL;
|
||||
tsl::elm::ListItem* ramBWItemCpu = NULL;
|
||||
tsl::elm::ListItem* ramBWItemGpu = NULL;
|
||||
tsl::elm::ListItem* ramBWItemMax = NULL;
|
||||
tsl::elm::ListItem* bqtempitem = NULL;
|
||||
tsl::elm::ListItem* aotagTempItem = NULL;
|
||||
tsl::elm::ListItem* cTypeItem = NULL;
|
||||
|
||||
ImageElement* CatImage = NULL;
|
||||
HideableCategoryHeader* CatHeader = NULL;
|
||||
HideableCustomDrawer* CatSpacer = NULL;
|
||||
int lightosClickCount = 0;
|
||||
|
||||
AboutGui::AboutGui()
|
||||
{
|
||||
memset(strings, 0, sizeof(strings));
|
||||
}
|
||||
|
||||
AboutGui::~AboutGui()
|
||||
{
|
||||
}
|
||||
|
||||
void AboutGui::listUI()
|
||||
{
|
||||
BaseMenuGui::refresh();
|
||||
|
||||
if (!this->context)
|
||||
return;
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Voltages")
|
||||
);
|
||||
|
||||
ramVoltItem =
|
||||
new tsl::elm::ListItem("RAM Voltage:");
|
||||
|
||||
if(IsMariko()) {
|
||||
this->listElement->addItem(ramVoltItem);
|
||||
}
|
||||
dispVoltItem =
|
||||
new tsl::elm::ListItem("Display Voltage:");
|
||||
this->listElement->addItem(dispVoltItem);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Temperatures")
|
||||
);
|
||||
eristaPLLXItem =
|
||||
new tsl::elm::ListItem("PLLX Temp:");
|
||||
if(this->context->temps[HocClkThermalSensor_AO] > 0) { // Only show if the value is valid (not -126, which means not patched)
|
||||
this->listElement->addItem(eristaPLLXItem);
|
||||
}
|
||||
|
||||
aotagTempItem =
|
||||
new tsl::elm::ListItem("AOTAG Temp:");
|
||||
this->listElement->addItem(aotagTempItem);
|
||||
|
||||
bqtempitem =
|
||||
new tsl::elm::ListItem("BQ24193 Temp:");
|
||||
this->listElement->addItem(bqtempitem);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("RAM Bandwidth")
|
||||
);
|
||||
|
||||
ramBWItemMax =
|
||||
new tsl::elm::ListItem("RAM BW (Peak):");
|
||||
this->listElement->addItem(ramBWItemMax);
|
||||
|
||||
ramBWItemAll =
|
||||
new tsl::elm::ListItem("RAM BW (All):");
|
||||
this->listElement->addItem(ramBWItemAll);
|
||||
|
||||
ramBWItemCpu =
|
||||
new tsl::elm::ListItem("RAM BW (CPU):");
|
||||
this->listElement->addItem(ramBWItemCpu);
|
||||
|
||||
ramBWItemGpu =
|
||||
new tsl::elm::ListItem("RAM BW (GPU):");
|
||||
this->listElement->addItem(ramBWItemGpu);
|
||||
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Hardware Info")
|
||||
);
|
||||
|
||||
cTypeItem =
|
||||
new tsl::elm::ListItem("Console Type:");
|
||||
this->listElement->addItem(cTypeItem);
|
||||
|
||||
SpeedoItem =
|
||||
new tsl::elm::ListItem("Speedo:");
|
||||
this->listElement->addItem(SpeedoItem);
|
||||
|
||||
IddqItem =
|
||||
new tsl::elm::ListItem("IDDQ:");
|
||||
this->listElement->addItem(IddqItem);
|
||||
|
||||
DramModule =
|
||||
new tsl::elm::ListItem("DRAM Module: ");
|
||||
this->listElement->addItem(DramModule);
|
||||
|
||||
waferCordsItem =
|
||||
new tsl::elm::ListItem("Wafer Position:");
|
||||
this->listElement->addItem(waferCordsItem);
|
||||
|
||||
if(IsHoag()) {
|
||||
RETROStatusItem =
|
||||
new tsl::elm::ListItem("RR Display status:");
|
||||
this->listElement->addItem(RETROStatusItem);
|
||||
}
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Software Info")
|
||||
);
|
||||
|
||||
// custRevItem = new tsl::elm::ListItem("CUST revision:");
|
||||
// this->listElement->addItem(custRevItem);
|
||||
|
||||
kipVersionItem = new tsl::elm::ListItem("KIP version:");
|
||||
this->listElement->addItem(kipVersionItem);
|
||||
|
||||
if(!IsHoag()) {
|
||||
sysdockStatusItem =
|
||||
new tsl::elm::ListItem("sys-dock status:");
|
||||
this->listElement->addItem(sysdockStatusItem);
|
||||
}
|
||||
|
||||
saltyNXStatusItem =
|
||||
new tsl::elm::ListItem("SaltyNX status:");
|
||||
this->listElement->addItem(saltyNXStatusItem);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Credits")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Developers")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Souldbminer")
|
||||
);
|
||||
|
||||
// Create special clickable item for Lightos
|
||||
auto lightosItem = new tsl::elm::ListItem("Lightos_");
|
||||
lightosItem->setClickListener([this](u64 keys) -> bool {
|
||||
if (keys & HidNpadButton_A) {
|
||||
lightosClickCount++;
|
||||
if (lightosClickCount >= 10) {
|
||||
if (CatImage != NULL) CatImage->setVisible(true);
|
||||
if (CatHeader != NULL) CatHeader->setVisible(true);
|
||||
if (CatSpacer != NULL) CatSpacer->setVisible(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
this->listElement->addItem(lightosItem);
|
||||
|
||||
// ---- Contributors ----
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Contributors")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Dom")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Blaise25")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("tetetete-ctrl")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("B3711")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("TDRR")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("MasaGratoR")
|
||||
);
|
||||
|
||||
// ---- Testers ----
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Testers")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Samybigio2011")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("arcdelta")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Miki1305")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Happy")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Winnerboi77")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Blaise25")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("WE1ZARD")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Alvise")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("agjeococh")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Xenshen")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("Frost")
|
||||
);
|
||||
|
||||
// ---- Special Thanks ----
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::CategoryHeader("Special Thanks")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("SciresM - Atmosphere CFW")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("KazushiMe - Switch OC Suite")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("hanai3Bi - Switch OC Suite & EOS")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("NaGaa95 - L4T-OC-Kernel")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("RetroNX - sys-clk")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("ppkantorski - Ultrahand")
|
||||
);
|
||||
|
||||
this->listElement->addItem(
|
||||
new tsl::elm::ListItem("CtCaer - Hekate, L4T and Proper Timings")
|
||||
);
|
||||
|
||||
// Create cat elements but hide them initially
|
||||
CatHeader = new HideableCategoryHeader("Cat");
|
||||
CatHeader->setVisible(false);
|
||||
this->listElement->addItem(CatHeader);
|
||||
|
||||
CatImage = new ImageElement(CAT_DATA, CAT_WIDTH, CAT_HEIGHT);
|
||||
CatImage->setVisible(false);
|
||||
this->listElement->addItem(CatImage);
|
||||
|
||||
CatSpacer = new HideableCustomDrawer(75);
|
||||
CatSpacer->setVisible(false);
|
||||
this->listElement->addItem(CatSpacer);
|
||||
}
|
||||
|
||||
std::string AboutGui::formatRamModule() {
|
||||
switch (this->context->dramID) {
|
||||
case 0: return "HB-MGCH 4GB";
|
||||
case 4: return "HM-MGCH 6GB";
|
||||
case 7: return "HM-MGXX 8GB";
|
||||
|
||||
case 1: return "NLE 4GB";
|
||||
case 2: return "WT:C 4GB";
|
||||
|
||||
case 3:
|
||||
case 5 ... 6: return "NEE 4GB";
|
||||
|
||||
case 8:
|
||||
case 12: return "AM-MGCJ 4GB";
|
||||
case 9:
|
||||
case 13: return "AM-MGCJ 8GB";
|
||||
|
||||
case 10:
|
||||
case 14: return "NME 4GB";
|
||||
|
||||
case 11:
|
||||
case 15: return "WT:E 4GB";
|
||||
|
||||
case 17:
|
||||
case 19:
|
||||
case 24: return "AA-MGCL 4GB";
|
||||
|
||||
case 18:
|
||||
case 23:
|
||||
case 28: return "AA-MGCL 8GB";
|
||||
|
||||
case 20 ... 22: return "AB-MGCL 4GB";
|
||||
|
||||
case 25 ... 27: return "WT:F 4GB";
|
||||
|
||||
case 29 ... 31: return "x267 4GB";
|
||||
|
||||
case 32 ... 34: return "WT:B 4GB";
|
||||
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void AboutGui::update()
|
||||
{
|
||||
BaseMenuGui::update();
|
||||
}
|
||||
|
||||
void AboutGui::refresh()
|
||||
{
|
||||
BaseMenuGui::refresh();
|
||||
|
||||
if (!this->context)
|
||||
return;
|
||||
// Format strings once per refresh
|
||||
sprintf(strings[0], "%u/%u/%u", this->context->speedos[HocClkSpeedo_CPU], this->context->speedos[HocClkSpeedo_GPU], this->context->speedos[HocClkSpeedo_SOC]);
|
||||
// This is how hekate does it
|
||||
sprintf(strings[1], "%u/%u/%u", this->context->iddq[HocClkSpeedo_CPU], this->context->iddq[HocClkSpeedo_GPU], this->context->iddq[HocClkSpeedo_SOC]);
|
||||
SpeedoItem->setValue(strings[0]);
|
||||
IddqItem->setValue(strings[1]);
|
||||
DramModule->setValue(formatRamModule());
|
||||
|
||||
// custRevItem->setValue(std::to_string(this->context->custRev));
|
||||
|
||||
kipVersionItem->setValue(std::to_string((this->context->kipVersion / 100) % 10) + "." + std::to_string((this->context->kipVersion / 10) % 10) + "." + std::to_string( this->context->kipVersion % 10) + " (Cust Rev " + std::to_string(this->context->custRev) + ")");
|
||||
if(!IsHoag())
|
||||
sysdockStatusItem->setValue(this->context->isSysDockInstalled ? "Installed" : "Not Installed");
|
||||
|
||||
saltyNXStatusItem->setValue(this->context->isSaltyNXInstalled ? "Installed" : "Not Installed");
|
||||
|
||||
if(IsHoag())
|
||||
RETROStatusItem->setValue(this->context->isUsingRetroSuper ? "Installed" : "Not Installed");
|
||||
|
||||
sprintf(strings[2], "X: %d Y: %d", this->context->waferX, this->context->waferY);
|
||||
waferCordsItem->setValue(strings[2]);
|
||||
|
||||
s32 millis = context->temps[HocClkThermalSensor_PLLX];
|
||||
sprintf(strings[3], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
|
||||
eristaPLLXItem->setValue(strings[3]);
|
||||
|
||||
millis = context->temps[HocClkThermalSensor_AO];
|
||||
if(millis > 0) {
|
||||
sprintf(strings[11], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
|
||||
} else if (millis == -125) {
|
||||
sprintf(strings[11], "Invalid");
|
||||
} else if (millis == -126) {
|
||||
sprintf(strings[11], "Not Patched");
|
||||
}
|
||||
aotagTempItem->setValue(strings[11]);
|
||||
|
||||
sprintf(strings[4], "%u.%u / %u mV", context->voltages[HocClkVoltage_EMCVDD2] / 1000U, (context->voltages[HocClkVoltage_EMCVDD2] % 1000U) / 100U, context->voltages[HocClkVoltage_EMCVDDQ] / 1000);
|
||||
ramVoltItem->setValue(strings[4]);
|
||||
|
||||
sprintf(strings[5], "%u.%u mV", context->voltages[HocClkVoltage_Display] / 1000U, (context->voltages[HocClkVoltage_Display] % 1000U) / 100U);
|
||||
dispVoltItem->setValue(strings[5]);
|
||||
|
||||
sprintf(strings[6], "%u MB/s", context->partLoad[HocClkPartLoad_RamBWAll]);
|
||||
ramBWItemAll->setValue(strings[6]);
|
||||
|
||||
sprintf(strings[7], "%u MB/s", context->partLoad[HocClkPartLoad_RamBWCpu]);
|
||||
ramBWItemCpu->setValue(strings[7]);
|
||||
|
||||
sprintf(strings[8], "%u MB/s", context->partLoad[HocClkPartLoad_RamBWGpu]);
|
||||
ramBWItemGpu->setValue(strings[8]);
|
||||
|
||||
sprintf(strings[9], "%u MB/s", context->partLoad[HocClkPartLoad_RamBWPeak]);
|
||||
ramBWItemMax->setValue(strings[9]);
|
||||
|
||||
switch(context->temps[HocClkThermalSensor_BQ24193]) {
|
||||
case BQ24193Temp_Normal:
|
||||
strcpy(strings[10], "Normal");
|
||||
break;
|
||||
case BQ24193Temp_Warm:
|
||||
strcpy(strings[10], "Warm");
|
||||
break;
|
||||
case BQ24193Temp_Hot:
|
||||
strcpy(strings[10], "Hot");
|
||||
break;
|
||||
case BQ24193Temp_Overheat:
|
||||
strcpy(strings[10], "Overheat");
|
||||
break;
|
||||
default:
|
||||
strcpy(strings[10], "Unknown");
|
||||
}
|
||||
|
||||
bqtempitem->setValue(strings[10]);
|
||||
|
||||
cTypeItem->setValue(hocClkFormatConsoleType(this->context->consoleType, true));
|
||||
|
||||
}
|
||||
@@ -1,552 +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 "config_info_strings.h"
|
||||
|
||||
std::vector<std::string> ConfigInfoStrings(HocClkConfigValue val, bool isMariko, bool isHoag)
|
||||
{
|
||||
switch (val)
|
||||
{
|
||||
case HocClkConfigValue_PollingIntervalMs:
|
||||
return {
|
||||
"The interval (in milliseconds) where clocks are applied, temperatures and voltages are polled and logs are written (if enabled).",
|
||||
"Higher values may cause more delay between changing a setting and it taking effect, and lower values may increase sysmodule memory usage",
|
||||
"Default: 300ms"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_RamDisplayUnit:
|
||||
return {
|
||||
"The unit used when displaying RAM frequency values.",
|
||||
"Options:",
|
||||
"- MHz: Megahertz (e.g. 1600 MHz)",
|
||||
"- MT/s: MegaTransfers per second (e.g. 3200 MT/s)",
|
||||
"- MHz and MT/s: Display in both MHz and MT/s",
|
||||
"Default: MHz"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_RAMVoltDisplayMode:
|
||||
return {
|
||||
"The method used to display RAM voltage values.",
|
||||
"Options:",
|
||||
"- VDD2 - Display VDD2 voltage",
|
||||
"- VDDQ - Display VDDQ voltage",
|
||||
"Default: VDD2"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_EnableExperimentalSettings:
|
||||
return {
|
||||
"When enabled, shows settings that are still being tested and may be unstable or not work at all.",
|
||||
"Use with caution and report any issues to the developers."
|
||||
};
|
||||
|
||||
case HocClkConfigValue_MarikoMiddleFreqs:
|
||||
return {
|
||||
"Allows usage of frequencies stepped by 38.4MHz instead of 76.8MHz below 1228MHz GPU clock",
|
||||
"Default: OFF"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_LiveCpuUv:
|
||||
return {
|
||||
"Allows changing CPU undervolt settings without a reboot",
|
||||
"Default: OFF"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_GPUSchedulingMethod:
|
||||
return {
|
||||
"Method used for GPU scheduling override",
|
||||
"Options:",
|
||||
"- INI: override via system_settings.ini",
|
||||
"- NV Service: override via nvservices sysmodule (experimental)",
|
||||
"Default: INI"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
|
||||
return {
|
||||
"How the RAM real frequency is measured",
|
||||
"Options:",
|
||||
"- PLL: Measure from PLLMB and PLLM (more accurate)",
|
||||
"- Actmon: Measure from Actmon (less accurate)",
|
||||
"Default: PLL"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_BatteryChargeCurrent:
|
||||
return {
|
||||
"Overrides the charge current to the battery. Use with caution!",
|
||||
isHoag ? "Default: 1664 mA" : "2048 mA"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_AulaDisplayColorPreset:
|
||||
return {
|
||||
"Current display color preset. Default is Basic",
|
||||
"Options:",
|
||||
"- Saturated: Based on Vivid but over-saturated.",
|
||||
"- Washed: Washed out colors.",
|
||||
"- Basic: Real natural profile.",
|
||||
"- Natural: Not actually natural.. Extra saturation.",
|
||||
"- Vivid: Saturated.",
|
||||
"Default: Do not override"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_CpuGovernorMinimumFreq:
|
||||
return {
|
||||
"The minimum frequency that the CPU governor will allow.",
|
||||
"Default: 612MHz"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_OverwriteRefreshRate:
|
||||
return {
|
||||
"Controls the availability of display refresh rate features.",
|
||||
"When enabled, allows changing the display refresh rate and using display refresh rate related features.",
|
||||
"This feature conflicts with FPSLocker's feature that does the same thing.",
|
||||
"Default: OFF"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_MaxDisplayClockH:
|
||||
return {
|
||||
"The maximum display clock frequency in handheld mode (in Hz).",
|
||||
"Warning: Changing this value may cause instability or display damage.",
|
||||
"Default: 60 Hz"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_DisplayVoltage:
|
||||
return {
|
||||
"The voltage supplied to the display panel (in mV).",
|
||||
"Warning: Changing this value may cause instability.",
|
||||
"Default: 1200mV"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_UncappedClocks:
|
||||
if(isMariko) {
|
||||
return {
|
||||
"When enabled, disables clock cappings",
|
||||
"Warning: Enabling this may cause damage to your device without a proper undervolt. Use with caution!",
|
||||
"Clock cappings:",
|
||||
"- Handheld:",
|
||||
" - GPU (HiOPT): 614 MHz",
|
||||
" - GPU (HiOPT - 15mV): 691 MHz",
|
||||
" - GPU (High UV): 768 MHz",
|
||||
"- USB Charger",
|
||||
" - GPU (HiOPT): 844 MHz",
|
||||
" - GPU (HiOPT - 15mV): 921 MHz",
|
||||
" - GPU (High UV): 998 MHz",
|
||||
"- PD Charger / Docked:",
|
||||
" - No capping applied",
|
||||
"Default: OFF"
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
"When enabled, disables clock cappings",
|
||||
"Warning: Enabling this may cause damage to your device without a proper undervolt. Use with caution!",
|
||||
"Clock cappings:",
|
||||
"- Handheld:",
|
||||
" - GPU: 460 MHz",
|
||||
" - CPU: 1581 MHz",
|
||||
"- USB Charger",
|
||||
" - GPU: 768 MHz",
|
||||
"- PD Charger / Docked:",
|
||||
" - No capping applied",
|
||||
"Default: OFF"
|
||||
};
|
||||
}
|
||||
|
||||
case HocClkConfigValue_ThermalThrottle:
|
||||
return {
|
||||
"If enabled, Resets to stock clocks after the threshold is applied",
|
||||
"Default: ON",
|
||||
};
|
||||
|
||||
case HocClkConfigValue_HandheldTDP:
|
||||
return {
|
||||
"If enabled, Resets to stock clocks when power consumption is above the threshold in handheld mode",
|
||||
"Default: ON",
|
||||
};
|
||||
|
||||
case HocClkConfigValue_HandheldTDPLimit:
|
||||
case HocClkConfigValue_LiteTDPLimit:
|
||||
return {
|
||||
"The power consumption threshold (in mW) for resetting to stock clocks in handheld mode when Handheld TDP is enabled.",
|
||||
isHoag ? "Default: 6400mW" : "Default: 9600mW"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_ThermalThrottleThreshold:
|
||||
return {
|
||||
"The temperature threshold (in °C) for resetting to stock clocks when Thermal Throttle is enabled.",
|
||||
"Default: 70°C"
|
||||
};
|
||||
|
||||
case KipConfigValue_emcDvbShift:
|
||||
return {
|
||||
"Each level adds/removes 25mV from the SOC Voltage table",
|
||||
"Consoles are bracketed by SoC Speedo. The brackets are as follows:",
|
||||
" - Speedo 1487-1598: Bracket 0",
|
||||
" - Speedo 1598-1709: Bracket 1",
|
||||
" - Speedo 1709-1820: Bracket 2",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoSocVmax:
|
||||
return {
|
||||
"The maximum available SOC Voltage that the DVB-adjusted table can use",
|
||||
"Default: Do not override"
|
||||
};
|
||||
|
||||
case KipConfigValue_hpMode:
|
||||
return {
|
||||
"When enabled, disables RAM powerdown. Helps with latency significantly",
|
||||
"Default: OFF"
|
||||
};
|
||||
|
||||
case KipConfigValue_commonEmcMemVolt:
|
||||
return {
|
||||
"RAM VDD2 voltage",
|
||||
"Increasing this WILL NOT increase your maximum frequency, but may help with timing reduction.",
|
||||
"Undervolting RAM is pointless and may hurt performance and stability",
|
||||
"Default: 1175 mV"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoEmcVddqVolt:
|
||||
return {
|
||||
"RAM VDDQ voltage",
|
||||
"Increasing this may help, but the default value is usually good enough.",
|
||||
"Undervolting RAM is pointless and may hurt performance and stability",
|
||||
"Default: 600 mV"
|
||||
};
|
||||
|
||||
case KipConfigValue_stepMode:
|
||||
return {
|
||||
"The step that RAM clocks take.",
|
||||
"Options (with examples):",
|
||||
" - 66MHz - 66 MHz step (ex. 1600, 1666, 1733, etc.)",
|
||||
" - 100MHz - 100 MHz step (ex. 1600, 1700, 1800, etc.)",
|
||||
" - 133MHz - 66 MHz step (ex. 1600, 1733, 1866, etc.)",
|
||||
" - JEDEC:",
|
||||
" - 1600, 1866, 1996, 2133, 2400, 2666, 2933 and 3200 MHz are used",
|
||||
"The RAM max clock will always be available regardless of the step mode, but the intermediate frequencies will be limited by the selected step mode.",
|
||||
"This setting does not affect performance and the option you choose mostly is based on your personal taste",
|
||||
"33 MHz step mode is not possible due to certain limitations of Horizon OS",
|
||||
"Default: 66 MHz",
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoEmcMaxClock:
|
||||
return {
|
||||
"The maximum RAM frequency available.",
|
||||
"Higher frequencies may cause instability, so increase this gradually and test for stability.",
|
||||
"Default: 2133 MHz"
|
||||
};
|
||||
|
||||
case KipConfigValue_eristaEmcMaxClock:
|
||||
case KipConfigValue_eristaEmcMaxClock1:
|
||||
case KipConfigValue_eristaEmcMaxClock2:
|
||||
return {
|
||||
"The RAM frequency used in the particular slot. Higher frequencies may cause instability, so increase this gradually and test for stability.",
|
||||
"Default: Disabled (1600 MHz)"
|
||||
};
|
||||
|
||||
case KipConfigValue_t1_tRCD:
|
||||
return {
|
||||
"RAS-to-CAS delay",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_t2_tRP:
|
||||
return {
|
||||
"Row precharge time",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_t3_tRAS:
|
||||
return {
|
||||
"Row active time",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_t4_tRRD:
|
||||
return {
|
||||
"Row refresh time",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_t5_tRFC:
|
||||
return {
|
||||
"Refresh Cycle Time",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_t6_tRTW:
|
||||
return {
|
||||
"Read To Write (High bracket)",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_t7_tWTR:
|
||||
return {
|
||||
"Write To Read (High bracket)",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_t8_tREFI:
|
||||
return {
|
||||
"Refresh command interval",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_timingEmcTbreak:
|
||||
return {
|
||||
"Frequency where t6 and t7 break between the low and high brackets",
|
||||
"Example:",
|
||||
"Tbreak is set to 1866 MHz, and t6Low is set to 4 and t6High is set to 2. At frequencies below 1866 MHz, t6 will be 4, and at frequencies above 1866 MHz, t6 will be 2.",
|
||||
"Default: Disabled"
|
||||
};
|
||||
|
||||
case KipConfigValue_low_t6_tRTW:
|
||||
return {
|
||||
"Read To Write (Low bracket)",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_low_t7_tWTR:
|
||||
return {
|
||||
"Write To Read (Low bracket)",
|
||||
"Default: 0"
|
||||
};
|
||||
case KipConfigValue_t2_tRP_cap:
|
||||
return {
|
||||
"Cap for t2 when 1333WL is used.",
|
||||
"The default value is sufficient for most RAMs but some may need a lower value",
|
||||
"Default: 2"
|
||||
};
|
||||
|
||||
case KipConfigValue_t6_tRTW_fine_tune:
|
||||
return {
|
||||
"Fine-tunes the raw calculation of t6",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_t7_tWTR_fine_tune:
|
||||
return {
|
||||
"Fine-tunes the raw calculation of t7",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_write_latency_1333:
|
||||
case KipConfigValue_write_latency_1600:
|
||||
case KipConfigValue_write_latency_1866:
|
||||
case KipConfigValue_write_latency_2133:
|
||||
case KipConfigValue_read_latency_1333:
|
||||
case KipConfigValue_read_latency_1600:
|
||||
case KipConfigValue_read_latency_1866:
|
||||
case KipConfigValue_read_latency_2133:
|
||||
return {
|
||||
"Latency bracket settings",
|
||||
"Example:",
|
||||
"If 1333 is set to 2000 MHz, 1600 set to 2500 MHz, 1866 set to 2766 MHz and 2133 set to 2933 MHz:",
|
||||
"Frequencies below 2000 MHz use 1333, 2033-2500 MHz use 1600, 2533-2766 MHz use 1866 and 2800-2933 MHz use 2133. ",
|
||||
"Either of these can be omitted and it will work (say you set 1333 to -, then <2000 MHz will use 1600 latency)",
|
||||
"If all of these parameters are omitted the latency will automatically be calculated as follows:",
|
||||
"1633-1866 MHz - 1866 WRL",
|
||||
"1900+ MHz - 2133 WRL",
|
||||
"These properties apply for both write and read latencies, and you can mix-and-match the brackets if necessary",
|
||||
"Default: -"
|
||||
};
|
||||
|
||||
case KipConfigValue_mem_burst_read_latency:
|
||||
return {
|
||||
"The read latency for the ram",
|
||||
"Default: 1600 RL"
|
||||
};
|
||||
|
||||
case KipConfigValue_mem_burst_write_latency:
|
||||
return {
|
||||
"The write latency for the ram",
|
||||
"Default: 1600 WL"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoCpuUVLow:
|
||||
return {
|
||||
"The CPU UV level used before tBreak",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoCpuUVHigh:
|
||||
return {
|
||||
"The CPU UV level used after tBreak",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_tableConf:
|
||||
return {
|
||||
"The current UV table used. The tbreaks are as follows:",
|
||||
"1581 MHz tBreak and 1683 MHz tBreak use their respective tBreaks",
|
||||
"The other tables use 1581 MHz as tBreak",
|
||||
"The \"Default\" table does not contain frequencies past 1963 MHz and may not undervolt correctly"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoCpuLowVmin:
|
||||
return {
|
||||
"The CPU vmin used before tBreak",
|
||||
"Default: 620 mV"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoCpuHighVmin:
|
||||
return {
|
||||
"The CPU vmin used after tBreak",
|
||||
"Default: 750 mV"
|
||||
};
|
||||
|
||||
|
||||
case KipConfigValue_marikoCpuMaxVolt:
|
||||
return {
|
||||
"The maximum voltage that the CPU can use",
|
||||
"Change this setting with caution!",
|
||||
"Default: 1120 mV"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoCpuMaxClock:
|
||||
return {
|
||||
"The maximum available CPU clock",
|
||||
"Default: 1963 MHz"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoCpuBoostClock:
|
||||
return {
|
||||
"The clock used for the CPU in \"boost mode\"",
|
||||
"Default: 1963 MHz"
|
||||
};
|
||||
|
||||
case KipConfigValue_eristaCpuUV:
|
||||
return {
|
||||
"CPU undervolt level",
|
||||
"Default: 0"
|
||||
};
|
||||
|
||||
case KipConfigValue_eristaCpuUnlock:
|
||||
return {
|
||||
"Unlock unsafe CPU clocks",
|
||||
"Default: OFF"
|
||||
};
|
||||
|
||||
case KipConfigValue_eristaCpuVmin:
|
||||
return {
|
||||
"Minimum CPU voltage",
|
||||
"Default: 825 mV"
|
||||
};
|
||||
|
||||
case KipConfigValue_eristaCpuMaxVolt:
|
||||
return {
|
||||
"Maximum CPU voltage",
|
||||
"Default: 1235 mV"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_EristaMaxCpuClock:
|
||||
return {
|
||||
"The maximum available CPU clock",
|
||||
"Default: 1785 MHz"
|
||||
};
|
||||
|
||||
case KipConfigValue_eristaCpuBoostClock:
|
||||
return {
|
||||
"The clock used for the CPU in \"boost mode\"",
|
||||
"Default: 1785 MHz"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_OverwriteBoostMode:
|
||||
return {
|
||||
"If enabled, profiles can override the boost mode setting",
|
||||
"Default: OFF"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoGpuUV:
|
||||
return {
|
||||
"GPU undervolt level",
|
||||
"Options:",
|
||||
" - HiOPT: L4T Custom HiOPT table",
|
||||
" - HiOPT - 15mV: L4T Custom HiOPT table with a 15mV offset",
|
||||
" - High UV: The highest undervolt table, recommended",
|
||||
"Default: HiOPT"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoGpuVmin:
|
||||
return {
|
||||
"Minimum GPU voltage",
|
||||
"Note: DVFS may change this value when the RAM clock is changed if the DVFS mode is set to PCV Hijack",
|
||||
"Default: 610 mV"
|
||||
};
|
||||
|
||||
case KipConfigValue_marikoGpuVmax:
|
||||
return {
|
||||
"Maximum GPU voltage",
|
||||
"Default: 800 mV"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_DVFSMode:
|
||||
return {
|
||||
"The mode used for GPU DVFS",
|
||||
"Adjusts GPU vmin when RAM clock is changed due to a higher requirement",
|
||||
"Options:",
|
||||
"- Disabled: disabled...",
|
||||
"- PCV Hijack: hijack PCV for override",
|
||||
"Default: PCV Hijack"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_DVFSOffset:
|
||||
return {
|
||||
"The offset added/subtracted to the GPU vmin when the RAM clock is changed due to a higher requirement in PCV Hijack mode",
|
||||
"Default: 0 mV (Disabled)"
|
||||
};
|
||||
|
||||
case KipConfigValue_eristaGpuUV:
|
||||
return {
|
||||
"GPU undervolt level",
|
||||
"Options:",
|
||||
" - HiOPT: L4T Custom HiOPT table",
|
||||
" - HiOPT - 15mV: L4T Custom HiOPT table with a 15mV offset",
|
||||
" - High UV: The highest undervolt table, recommended",
|
||||
"Default: HiOPT"
|
||||
};
|
||||
|
||||
case KipConfigValue_eristaGpuVmin:
|
||||
return {
|
||||
"Minimum GPU voltage",
|
||||
"Default: 810 mV (812mV as erista is stepped by 6.5mV instead of 5mV)"
|
||||
};
|
||||
|
||||
case KipConfigValue_commonGpuVoltOffset:
|
||||
return {
|
||||
"The offset added/subtracted to all AUTO GPU voltages",
|
||||
"Default: 0 mV (Disabled)"
|
||||
};
|
||||
|
||||
case HocClkConfigValue_GPUScheduling:
|
||||
return {
|
||||
"The scheduling method used for GPU clocks",
|
||||
"Options:",
|
||||
"- Do Not Override: Do not override existing scheduling mode",
|
||||
"- Disabled: Disables GPU scheduling, 99.7% GPU max load",
|
||||
"- Enabled: Enables GPU scheduling, 96.5% GPU max load",
|
||||
"Default: Do not override"
|
||||
};
|
||||
case KipConfigValue_marikoGpuBootVolt:
|
||||
return {
|
||||
"The voltage supplied to the GPU during boot and when the temperature is below 20°C (in mV).",
|
||||
"Warning: Changing this value may cause instability.",
|
||||
"Default: 800mV"
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -1,88 +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 "info_gui.h"
|
||||
#include "ult_ext.h"
|
||||
#include <sstream>
|
||||
|
||||
InfoGui::InfoGui(std::string title, std::vector<std::string> strings)
|
||||
: m_title(std::move(title)), m_strings(std::move(strings)) {}
|
||||
|
||||
static constexpr s32 TEXT_SIZE = 16;
|
||||
static constexpr s32 LINE_H = 22;
|
||||
static constexpr s32 PARA_GAP = 10;
|
||||
static constexpr s32 MARGIN_L = 20;
|
||||
static constexpr s32 MARGIN_R = 35;
|
||||
|
||||
static std::vector<std::string> wrapText(const std::string& text, s32 maxWidth)
|
||||
{
|
||||
constexpr float CHAR_W = 10.0f;
|
||||
|
||||
// Preserve leading whitespace as an indent prefix for wrapped continuation lines.
|
||||
std::string indent;
|
||||
for (char c : text) {
|
||||
if (c == ' ') indent += ' ';
|
||||
else break;
|
||||
}
|
||||
|
||||
std::vector<std::string> lines;
|
||||
std::istringstream ss(text);
|
||||
std::string word, line = indent; // seed with indent so first word inherits it
|
||||
bool first = true;
|
||||
while (ss >> word) {
|
||||
std::string candidate = (first && !indent.empty()) ? indent + word
|
||||
: line.empty() ? word
|
||||
: line + " " + word;
|
||||
first = false;
|
||||
if (static_cast<s32>(candidate.size() * CHAR_W) <= maxWidth)
|
||||
line = std::move(candidate);
|
||||
else {
|
||||
if (!line.empty() && line != indent) lines.push_back(line);
|
||||
line = indent + word;
|
||||
}
|
||||
}
|
||||
if (!line.empty() && line != indent) lines.push_back(line);
|
||||
if (lines.empty()) lines.emplace_back("");
|
||||
return lines;
|
||||
}
|
||||
|
||||
void InfoGui::listUI()
|
||||
{
|
||||
this->listElement->addItem(new tsl::elm::CategoryHeader(m_title));
|
||||
|
||||
const s32 maxWidth = tsl::cfg::FramebufferWidth - MARGIN_L - MARGIN_R;
|
||||
|
||||
for (const auto& para : m_strings) {
|
||||
for (const auto& lineText : wrapText(para, maxWidth)) {
|
||||
auto* d = new FocusableDrawer(
|
||||
[lineText](tsl::gfx::Renderer* r, s32 x, s32 y, s32 w, s32 h) {
|
||||
r->drawString((lineText + "\n").c_str(), false,
|
||||
x + MARGIN_L, y + LINE_H - 5,
|
||||
TEXT_SIZE, tsl::style::color::ColorText);
|
||||
});
|
||||
d->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, LINE_H);
|
||||
this->listElement->addItem(d, LINE_H);
|
||||
}
|
||||
|
||||
// paragraph gap
|
||||
auto* gap = new tsl::elm::CustomDrawer(
|
||||
[](tsl::gfx::Renderer*, s32, s32, s32, s32) {});
|
||||
gap->setBoundaries(0, 0, tsl::cfg::FramebufferWidth, PARA_GAP);
|
||||
this->listElement->addItem(gap, PARA_GAP);
|
||||
}
|
||||
}
|
||||
@@ -1,136 +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"},
|
||||
{345600000, "Handheld"},
|
||||
{384000000, "Handheld"},
|
||||
{422400000, "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];
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,82 +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 "../../ipc.h"
|
||||
#include "base_menu_gui.h"
|
||||
#include <initializer_list>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "freq_choice_gui.h"
|
||||
#include "value_choice_gui.h"
|
||||
#include "info_gui.h"
|
||||
class MiscGui : public BaseMenuGui
|
||||
{
|
||||
public:
|
||||
MiscGui();
|
||||
~MiscGui();
|
||||
void listUI() override;
|
||||
void refresh() override;
|
||||
|
||||
protected:
|
||||
HocClkConfigValueList* configList;
|
||||
std::map<HocClkConfigValue, tsl::elm::ListItem*> configButtons;
|
||||
std::map<HocClkConfigValue, ValueRange> configRanges;
|
||||
std::map<HocClkConfigValue, std::vector<NamedValue>> configNamedValues;
|
||||
std::map<HocClkConfigValue, tsl::elm::ToggleListItem*> configToggles;
|
||||
std::map<HocClkConfigValue, std::tuple<tsl::elm::TrackBar*, tsl::elm::ListItem*, std::vector<uint64_t>>> configTrackbars;
|
||||
std::set<HocClkConfigValue> configButtonSKeys;
|
||||
std::map<HocClkConfigValue, std::string> configButtonSSubtext;
|
||||
std::set<HocClkConfigValue> emcClockConfigs;
|
||||
|
||||
void addConfigToggle(HocClkConfigValue configVal, const char* altName, bool kip = false);
|
||||
void addConfigTrackbar(HocClkConfigValue configVal, const char* altName, const ValueRange& range, bool kip = true);
|
||||
void addMappedConfigTrackbar(HocClkConfigValue configVal, const char* altName,
|
||||
std::vector<u32> vals,
|
||||
std::initializer_list<std::string> names, bool kip = true);
|
||||
void addConfigButton(HocClkConfigValue configVal,
|
||||
const char* altName,
|
||||
const ValueRange& range,
|
||||
const std::string& categoryName,
|
||||
const ValueThresholds* thresholds,
|
||||
const std::map<uint32_t, std::string>& labels = {},
|
||||
const std::vector<NamedValue>& namedValues = {},
|
||||
bool showDefaultValue = true,
|
||||
bool kip = false);
|
||||
|
||||
void addConfigButtonS(HocClkConfigValue configVal,
|
||||
const char* altName,
|
||||
const ValueRange& range,
|
||||
const std::string& categoryName,
|
||||
const ValueThresholds* thresholds,
|
||||
const std::map<uint32_t, std::string>& labels = {},
|
||||
const std::vector<NamedValue>& namedValues = {},
|
||||
bool showDefaultValue = true,
|
||||
const char* subText = nullptr,
|
||||
bool kip = false);
|
||||
void addFreqButton(HocClkConfigValue configVal,
|
||||
const char* altName,
|
||||
HocClkModule module,
|
||||
const std::map<uint32_t, std::string>& labels = {});
|
||||
void updateConfigToggles();
|
||||
|
||||
tsl::elm::ToggleListItem* enabledToggle;
|
||||
u8 frameCounter = 60;
|
||||
bool shouldSaveKip = false;
|
||||
};
|
||||
@@ -1,273 +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 "../hos/apm_ext.h"
|
||||
#include <i2c.h>
|
||||
#include <t210.h>
|
||||
#include <max17050.h>
|
||||
#include <tmp451.h>
|
||||
#include <ipc_server.h>
|
||||
#include <lockable_mutex.h>
|
||||
#include <hocclk.h>
|
||||
#include <switch.h>
|
||||
#include <pwm.h>
|
||||
#include <registers.h>
|
||||
#include <battery.h>
|
||||
#include "../display/display_refresh_rate.hpp"
|
||||
#include <notification.h>
|
||||
|
||||
#include "board.hpp"
|
||||
#include "board_fuse.hpp"
|
||||
#include "board_load.hpp"
|
||||
#include "board_volt.hpp"
|
||||
#include "board_misc.hpp"
|
||||
#include "../tsensor/soctherm.hpp"
|
||||
#include "../tsensor/aotag.hpp"
|
||||
#include "../hos/integrations.hpp"
|
||||
#include "../file/file_utils.hpp"
|
||||
#include "../hos/rgltr.h"
|
||||
namespace board {
|
||||
|
||||
u64 clkVirtAddr, dsiVirtAddr, apbVirtAddr, fuseVirtAddr;
|
||||
|
||||
HocClkSocType gSocType;
|
||||
u8 gDramID;
|
||||
HocClkConsoleType gConsoleType = HocClkConsoleType_Icosa;
|
||||
FuseData fuseData;
|
||||
u8 speedoBracket;
|
||||
PwmChannelSession iCon;
|
||||
|
||||
u32 fd = 0, fd2 = 0;
|
||||
|
||||
#define PMC_BASE 0x7000E400
|
||||
#define APB_MISC_GP_HIDREV 0x804
|
||||
#define GP_HIDREV_MAJOR_T210 0x1
|
||||
#define GP_HIDREV_MAJOR_T210B01 0x2
|
||||
#define APB_BASE 0x70000000
|
||||
#define FUSE_RESERVED_ODMX(x) (0x1C8 + 4 * (x))
|
||||
#define FUSE_OFFSET 0x800
|
||||
void FetchHardwareInfos() {
|
||||
ReadFuses(fuseData, fuseVirtAddr);
|
||||
SetGpuBracket(fuseData.gpuSpeedo, speedoBracket);
|
||||
|
||||
u32 hidrev = *(u32*)(apbVirtAddr + APB_MISC_GP_HIDREV);
|
||||
if (((hidrev >> 4) & 0xF) >= GP_HIDREV_MAJOR_T210B01) {
|
||||
gSocType = HocClkSocType_Mariko;
|
||||
CacheGpuVoltTable();
|
||||
} else {
|
||||
gSocType = HocClkSocType_Erista;
|
||||
}
|
||||
|
||||
u32 odm4 = *(u32*)(fuseVirtAddr + FUSE_OFFSET + FUSE_RESERVED_ODMX(4));
|
||||
|
||||
if (gSocType == HocClkSocType_Mariko) {
|
||||
switch ((odm4 & 0xF0000) >> 16) {
|
||||
case 2:
|
||||
gConsoleType = HocClkConsoleType_Hoag;
|
||||
break;
|
||||
case 4:
|
||||
gConsoleType = HocClkConsoleType_Aula;
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
gConsoleType = HocClkConsoleType_Iowa;
|
||||
}
|
||||
} else {
|
||||
gConsoleType = HocClkConsoleType_Icosa;
|
||||
}
|
||||
|
||||
gDramID = (odm4 & 0xF8) >> 3;
|
||||
// Get extended dram id info.
|
||||
if (gSocType == HocClkSocType_Mariko) {
|
||||
gDramID |= (odm4 & 0x7000) >> 7;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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");
|
||||
|
||||
rc = pmdmntInitialize();
|
||||
ASSERT_RESULT_OK(rc, "pmdmntInitialize");
|
||||
|
||||
rc = rgltrInitialize();
|
||||
ASSERT_RESULT_OK(rc, "rgltrInitialize");
|
||||
|
||||
rc = QueryMemoryMapping(&clkVirtAddr, 0x60006000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (clk)");
|
||||
|
||||
rc = QueryMemoryMapping(&dsiVirtAddr, 0x54300000, 0x40000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (dsi)");
|
||||
|
||||
rc = QueryMemoryMapping(&apbVirtAddr, 0x70000000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (apb)");
|
||||
|
||||
rc = QueryMemoryMapping(&fuseVirtAddr, 0x7000F000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (fuse)");
|
||||
|
||||
FetchHardwareInfos();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
StartLoad(nvCheck, fd);
|
||||
|
||||
batteryInfoInitialize();
|
||||
|
||||
tsensor::InitializeSoctherm(); // SOCTHERM must be init before AOTAG
|
||||
|
||||
// PMC exosphere check
|
||||
SecmonArgs args = {};
|
||||
args.X[0] = 0xF0000002;
|
||||
args.X[1] = PMC_BASE;
|
||||
svcCallSecureMonitor(&args);
|
||||
|
||||
if (args.X[1] != PMC_BASE) { // if param 1 is identical read failed
|
||||
tsensor::InitializeAotag(GetSocType() == HocClkSocType_Mariko);
|
||||
}
|
||||
|
||||
Result pwmCheck = 1;
|
||||
if (hosversionAtLeast(6,0,0) && R_SUCCEEDED(pwmInitialize())) {
|
||||
pwmCheck = pwmOpenSession2(&iCon, 0x3D000001);
|
||||
}
|
||||
|
||||
StartMiscThread(pwmCheck, &iCon);
|
||||
|
||||
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();
|
||||
rgltrExit();
|
||||
if (HOSSVC_HAS_TC) {
|
||||
tcExit();
|
||||
}
|
||||
|
||||
max17050Exit();
|
||||
tmp451Exit();
|
||||
display::Shutdown();
|
||||
|
||||
ExitLoad();
|
||||
|
||||
ExitMiscThread();
|
||||
|
||||
pwmChannelSessionClose(&iCon);
|
||||
pwmExit();
|
||||
batteryInfoExit();
|
||||
pmdmntExit();
|
||||
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-Lesen fehlgeschlagen!\nDies könnte ein Hardwareproblem sein!");
|
||||
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,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 <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 "../mapping/mem_map.hpp"
|
||||
#define HOSSVC_HAS_CLKRST (hosversionAtLeast(8,0,0))
|
||||
#define HOSSVC_HAS_TC (hosversionAtLeast(5,0,0))
|
||||
|
||||
namespace board {
|
||||
extern u64 clkVirtAddr, dsiVirtAddr, apbVirtAddr, fuseVirtAddr;
|
||||
extern HocClkSocType gSocType;
|
||||
extern u8 gDramID;
|
||||
extern HocClkConsoleType gConsoleType;
|
||||
extern FuseData fuseData;
|
||||
extern u8 speedoBracket;
|
||||
|
||||
void Initialize();
|
||||
void Exit();
|
||||
HocClkSocType GetSocType();
|
||||
HocClkConsoleType GetConsoleType();
|
||||
u8 GetDramID();
|
||||
u8 GetGpuSpeedoBracket();
|
||||
bool IsDram8GB();
|
||||
void SetDisplayRefreshDockedState(bool docked);
|
||||
FuseData *GetFuseData();
|
||||
bool IsUsingRetroSuperDisplay();
|
||||
|
||||
}
|
||||
@@ -1,262 +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 "../hos/apm_ext.h"
|
||||
#include <i2c.h>
|
||||
#include "../i2c/i2cDrv.h"
|
||||
#include <t210.h>
|
||||
#include <max17050.h>
|
||||
#include <tmp451.h>
|
||||
#include <ipc_server.h>
|
||||
#include <lockable_mutex.h>
|
||||
#include "../display/display_refresh_rate.hpp"
|
||||
#include "board.hpp"
|
||||
#include "board_name.hpp"
|
||||
#include "../file/errors.hpp"
|
||||
#include "../soc/pllmb.hpp"
|
||||
#include "../file/config.hpp"
|
||||
#include "../soc/gm20b.hpp"
|
||||
#include "../file/config.hpp"
|
||||
namespace board {
|
||||
#define MIDDLE_FREQ_TABLE_START_POINT 1228800000
|
||||
static u32 currentInjectedHz = 0;
|
||||
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 HandleCpuUv()
|
||||
{
|
||||
if (board::GetSocType() == HocClkSocType_Erista)
|
||||
board::SetDfllTunings(config::GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000); // Erista tbreak is always 1581MHz
|
||||
else
|
||||
board::SetDfllTunings(config::GetConfigValue(KipConfigValue_marikoCpuUVLow), config::GetConfigValue(KipConfigValue_marikoCpuUVHigh), board::CalculateTbreak(config::GetConfigValue(KipConfigValue_tableConf)));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool useGm20b = (module == HocClkModule_GPU) && (GetSocType() == HocClkSocType_Mariko) && (hz % 38400000 == 0) && (hz % 76800000 != 0) && hz < MIDDLE_FREQ_TABLE_START_POINT;
|
||||
|
||||
u32 pcvHz = useGm20b ? ((hz + 76800000 - 1) / 76800000) * 76800000 : hz;
|
||||
|
||||
if (module == HocClkModule_GPU)
|
||||
currentInjectedHz = 0;
|
||||
|
||||
if (HOSSVC_HAS_CLKRST) {
|
||||
ClkrstSession session = {};
|
||||
rc = clkrstOpenSession(&session, GetPcvModuleId(module), 3);
|
||||
ASSERT_RESULT_OK(rc, "clkrstOpenSession");
|
||||
ClkrstSetHz(session, pcvHz);
|
||||
|
||||
/* Voltage bug workaround. */
|
||||
if (module == HocClkModule_CPU) {
|
||||
svcSleepThread(300'000);
|
||||
ClkrstSetHz(session, pcvHz);
|
||||
}
|
||||
|
||||
clkrstCloseSession(&session);
|
||||
} else {
|
||||
PcvSetHz(GetPcvModule(module), pcvHz);
|
||||
|
||||
if (module == HocClkModule_CPU) {
|
||||
svcSleepThread(300'000);
|
||||
PcvSetHz(GetPcvModule(module), pcvHz);
|
||||
}
|
||||
}
|
||||
if(config::GetConfigValue(HocClkConfigValue_LiveCpuUv) && module == HocClkModule_CPU) {
|
||||
HandleCpuUv();
|
||||
}
|
||||
if (useGm20b) {
|
||||
gm20b::setClock(hz / 1000);
|
||||
currentInjectedHz = 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 (module == HocClkModule_GPU && currentInjectedHz != 0) {
|
||||
return currentInjectedHz;
|
||||
}
|
||||
|
||||
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 config::GetConfigValue(HocClkConfigValue_MemoryFrequencyMeasurementMode) == MemoryFrequencyMeasurementMode_PLL ? pllmb::getRamClockRatePLLMB() : t210ClkMemFreq();
|
||||
case HocClkModule_Display:
|
||||
return GetDisplayRate(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,97 +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 "../hos/apm_ext.h"
|
||||
#include <i2c.h>
|
||||
#include <t210.h>
|
||||
#include <max17050.h>
|
||||
#include <tmp451.h>
|
||||
#include <ipc_server.h>
|
||||
#include <lockable_mutex.h>
|
||||
#include "../file/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 HandleCpuUv();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user