Compare commits
479 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f90ba2ca59 | ||
|
|
f97fdc9528 | ||
|
|
ed63a95488 | ||
|
|
e24512c9d6 | ||
|
|
1efd91d535 | ||
|
|
3337738dcc | ||
|
|
7f1f504c36 | ||
|
|
2f21f23266 | ||
|
|
3c99ad0186 | ||
|
|
e4dd690ac9 | ||
|
|
9122768953 | ||
|
|
3539916cfd | ||
|
|
4bd776c8aa | ||
|
|
78cda43054 | ||
|
|
580db1f167 | ||
|
|
ea9adbfa14 | ||
|
|
accfea86f1 | ||
|
|
badf182529 | ||
|
|
12f12c6b1e | ||
|
|
dde723baee | ||
|
|
27db54644e | ||
|
|
138bcb6dc6 | ||
|
|
05fa5034d3 | ||
|
|
8a98a57b4b | ||
|
|
c12b0136f8 | ||
|
|
c4f7f0e713 | ||
|
|
935dd24129 | ||
|
|
d72e05259a | ||
|
|
847ae19de9 | ||
|
|
8651e418ca | ||
|
|
2e3ef0f3b0 | ||
|
|
c018b10a7f | ||
|
|
336ea0eddb | ||
|
|
d561c0e538 | ||
|
|
a7c1fe0d27 | ||
|
|
33582678c2 | ||
|
|
3ca1f17e4d | ||
|
|
65dfa8f48a | ||
|
|
90ea18c881 | ||
|
|
598136c64b | ||
|
|
848a788c5f | ||
|
|
d443e069fd | ||
|
|
b7e7f0f720 | ||
|
|
916e5c5ddf | ||
|
|
c766ab1569 | ||
|
|
9ee4d79f77 | ||
|
|
d55d1ab319 | ||
|
|
3be1eb6149 | ||
|
|
5e4f58975c | ||
|
|
d0c257dbc0 | ||
|
|
ffb0ded210 | ||
|
|
396304c738 | ||
|
|
8c6baf9e2f | ||
|
|
a24eec7256 | ||
|
|
77a45966da | ||
|
|
04d7e2f8e0 | ||
|
|
f5481d1c00 | ||
|
|
2e39421074 | ||
|
|
9cf901d487 | ||
|
|
0cdc96f7f8 | ||
|
|
73fafa6337 | ||
|
|
cfae441656 | ||
|
|
bf6bfa3e57 | ||
|
|
72f5e11c42 | ||
|
|
93b6974be5 | ||
|
|
1f545a7030 | ||
|
|
73bcb384b5 | ||
|
|
fab0b76a35 | ||
|
|
2f830bac53 | ||
|
|
5bf7fdf60e | ||
|
|
af4f00f682 | ||
|
|
d4b09f147b | ||
|
|
1b33e9982c | ||
|
|
c998aa78ae | ||
|
|
2b5a27f86e | ||
|
|
52b1b8da81 | ||
|
|
f409f5b8c3 | ||
|
|
77a7470149 | ||
|
|
1d9fa4c091 | ||
|
|
a16cf8a2ef | ||
|
|
89418b7cd0 | ||
|
|
00cf05d9c5 | ||
|
|
39e7ba6b75 | ||
|
|
d7f2cee3b1 | ||
|
|
3848bd7c83 | ||
|
|
693954930a | ||
|
|
de89768c74 | ||
|
|
8f243c8369 | ||
|
|
fd91f376c4 | ||
|
|
f0fe114303 | ||
|
|
1c64cf5db8 | ||
|
|
9eea39325d | ||
|
|
9bb6b3383b | ||
|
|
fb3e976f2e | ||
|
|
ec4a1f974b | ||
|
|
fc4b3b8352 | ||
|
|
5123f7b1de | ||
|
|
3251d104c4 | ||
|
|
ea2d604c20 | ||
|
|
ae71cd447d | ||
|
|
4261dd83f0 | ||
|
|
4dc01c024a | ||
|
|
9e26329173 | ||
|
|
36441e6dea | ||
|
|
cdf28607c9 | ||
|
|
b383fa7ec2 | ||
|
|
d7178ddd2c | ||
|
|
9d149f0939 | ||
|
|
170dd79347 | ||
|
|
d97d359ec2 | ||
|
|
9ff9e315a0 | ||
|
|
cde560c4bd | ||
|
|
a15860ba8d | ||
|
|
76e6f1bd27 | ||
|
|
2c1ad1b755 | ||
|
|
2f2e5e9fb8 | ||
|
|
65b77a2d0b | ||
|
|
5d4812bde5 | ||
|
|
addd2de4d9 | ||
|
|
0df8ce745f | ||
|
|
62d055f163 | ||
|
|
fa1510a40d | ||
|
|
e6942f95e7 | ||
|
|
94b63003ab | ||
|
|
8aa20dc0cf | ||
|
|
db92c60cd2 | ||
|
|
c8c7b233f5 | ||
|
|
5a9afcbb74 | ||
|
|
8127a0c3b7 | ||
|
|
7c5e746594 | ||
|
|
7d30c4d384 | ||
|
|
883b4fc3f4 | ||
|
|
530588a818 | ||
|
|
a6402bd5d5 | ||
|
|
cd3d29ce88 | ||
|
|
192b70dae4 | ||
|
|
aaa9f90794 | ||
|
|
d5e38b0eb3 | ||
|
|
3704d46136 | ||
|
|
b374117c37 | ||
|
|
c9c5d08919 | ||
|
|
c963dd8369 | ||
|
|
7fc9682de0 | ||
|
|
0c555f34c3 | ||
|
|
fb37d5f0e7 | ||
|
|
a7619c39d2 | ||
|
|
55b97156ce | ||
|
|
37ad65e768 | ||
|
|
3b4877b287 | ||
|
|
01d79aab30 | ||
|
|
3bb7b4cbd5 | ||
|
|
9c16f8b2b8 | ||
|
|
78112547b6 | ||
|
|
3c7a42b033 | ||
|
|
203587523a | ||
|
|
07d0824c55 | ||
|
|
f1eab00ce1 | ||
|
|
6a851d4095 | ||
|
|
822e9f2817 | ||
|
|
55b3a4230c | ||
|
|
be61b9df0e | ||
|
|
6c8d429c64 | ||
|
|
aa95f526b8 | ||
|
|
638ddb499c | ||
|
|
c95b6fde88 | ||
|
|
2dd726723e | ||
|
|
119f49a3a4 | ||
|
|
51f935c252 | ||
|
|
e1003520eb | ||
|
|
3b5185df8d | ||
|
|
26c1dd1f99 | ||
|
|
819913ffd9 | ||
|
|
923bd0c0ba | ||
|
|
185e5bcf31 | ||
|
|
d1ef905ac5 | ||
|
|
fad9b5be50 | ||
|
|
c6424403b3 | ||
|
|
2b23498285 | ||
|
|
c7db7a7632 | ||
|
|
985ecdc980 | ||
|
|
e1d75f0084 | ||
|
|
f276ca0187 | ||
|
|
ae39b1b1bd | ||
|
|
9321aed1d1 | ||
|
|
756c44ba82 | ||
|
|
037f011c9b | ||
|
|
d2a46e1202 | ||
|
|
976f133c97 | ||
|
|
accdf6dc34 | ||
|
|
b417099a4a | ||
|
|
b7440a38a5 | ||
|
|
6369382de1 | ||
|
|
65db0b4989 | ||
|
|
19b29b97e1 | ||
|
|
ec230e35d0 | ||
|
|
5942cfd68d | ||
|
|
66d0109a9a | ||
|
|
6334603f9e | ||
|
|
5d12c1721a | ||
|
|
7e42394894 | ||
|
|
07dd65eebf | ||
|
|
ca8bb25660 | ||
|
|
90e53b52b2 | ||
|
|
c6cd863526 | ||
|
|
24eb3b1208 | ||
|
|
9a6282af9c | ||
|
|
5de030c4e9 | ||
|
|
dc63dfdca2 | ||
|
|
803a838b35 | ||
|
|
5b284db5ae | ||
|
|
861ee85289 | ||
|
|
521e3c22dd | ||
|
|
78798a1cce | ||
|
|
44d417fb89 | ||
|
|
8faaaa58d9 | ||
|
|
df0969ba6f | ||
|
|
646f8b8d69 | ||
|
|
456684152a | ||
|
|
a606a878ee | ||
|
|
75531e6c4f | ||
|
|
fa7313c495 | ||
|
|
5816d49f68 | ||
|
|
589c41d8e5 | ||
|
|
b35971e265 | ||
|
|
1d3836fcbb | ||
|
|
e515df84a5 | ||
|
|
901cb15191 | ||
|
|
aaa00ca3d0 | ||
|
|
1f449af24f | ||
|
|
cb810367b8 | ||
|
|
27cd40a7e8 | ||
|
|
75480ffdb5 | ||
|
|
d3ee7c2976 | ||
|
|
3e5cec375d | ||
|
|
280392158e | ||
|
|
a8d47ae8c8 | ||
|
|
eb6364f994 | ||
|
|
59ba05332f | ||
|
|
fc203c723c | ||
|
|
5f427ec24d | ||
|
|
cf891190cd | ||
|
|
3411959537 | ||
|
|
cce2069a32 | ||
|
|
485aa83de5 | ||
|
|
52e8f5c584 | ||
|
|
234fb1655c | ||
|
|
78a52df596 | ||
|
|
8bffe2dfc1 | ||
|
|
064ecc6d25 | ||
|
|
2d5ba3a841 | ||
|
|
3b2de89d2f | ||
|
|
b959ee864b | ||
|
|
5f2d7a68a9 | ||
|
|
554b66e25f | ||
|
|
e20bafd6ab | ||
|
|
dc2252a7c6 | ||
|
|
80fa802e88 | ||
|
|
944c1ef1f2 | ||
|
|
da0806df5d | ||
|
|
eda88210a9 | ||
|
|
bb1ae1d816 | ||
|
|
0a2a4cbd6b | ||
|
|
2607034b7a | ||
|
|
5da2ec11db | ||
|
|
46d7b4e5fe | ||
|
|
4358df0308 | ||
|
|
4103777f48 | ||
|
|
bf17e53c8f | ||
|
|
8f6a5eee28 | ||
|
|
6d0de115eb | ||
|
|
1f2999df2f | ||
|
|
1f2b3848e4 | ||
|
|
955d009f54 | ||
|
|
6607c287db | ||
|
|
ce99462081 | ||
|
|
91c12b9128 | ||
|
|
99e5cfc97e | ||
|
|
ec661ac1c0 | ||
|
|
1de73c4b74 | ||
|
|
8a3b6f9775 | ||
|
|
7f0f743f47 | ||
|
|
5a0ae8da5b | ||
|
|
77fddda8a9 | ||
|
|
2828687a18 | ||
|
|
0c1f9e661c | ||
|
|
52aa25b212 | ||
|
|
3e98704a93 | ||
|
|
4a7277d8bd | ||
|
|
143de18f3a | ||
|
|
8626854d10 | ||
|
|
6bd3ba7b3d | ||
|
|
bc06d65b72 | ||
|
|
7045332ef2 | ||
|
|
35bc76074c | ||
|
|
cdb725dc86 | ||
|
|
215ccf9001 | ||
|
|
ec21e47b53 | ||
|
|
17e27ad6e9 | ||
|
|
7489a861f6 | ||
|
|
ef65b4dea9 | ||
|
|
0829cc8545 | ||
|
|
5fb0899bb8 | ||
|
|
c17d5a3999 | ||
|
|
42045c5c88 | ||
|
|
6e6f7b4f9f | ||
|
|
0496839500 | ||
|
|
87911b8b9e | ||
|
|
d3c441f8ca | ||
|
|
af6b347762 | ||
|
|
96b6459282 | ||
|
|
371577954b | ||
|
|
6bdb79fc15 | ||
|
|
e625e4251a | ||
|
|
5d7a77074b | ||
|
|
3b907d3ca0 | ||
|
|
a697cac43c | ||
|
|
3d5147a6e2 | ||
|
|
f46d0a714b | ||
|
|
3896c89a5c | ||
|
|
dd645bb352 | ||
|
|
9afd3d44bd | ||
|
|
5db6ac9f25 | ||
|
|
95930746d2 | ||
|
|
81e6fb9699 | ||
|
|
8b9fbdf13d | ||
|
|
901ab93e58 | ||
|
|
eb75fb2274 | ||
|
|
dab17f527e | ||
|
|
aabff0e948 | ||
|
|
610bf9ad82 | ||
|
|
1a42ad1a67 | ||
|
|
71f31e8f6a | ||
|
|
d6bf10d113 | ||
|
|
decd02b564 | ||
|
|
3b681f8c90 | ||
|
|
95e68bd27e | ||
|
|
dfd5af7e80 | ||
|
|
0afef60198 | ||
|
|
714eebb889 | ||
|
|
a5f58bfb3a | ||
|
|
c29d6244a4 | ||
|
|
130f9101a2 | ||
|
|
308f39694c | ||
|
|
9e916b5d0f | ||
|
|
c2610472ce | ||
|
|
2dbd436307 | ||
|
|
d525991beb | ||
|
|
b3ff426bfe | ||
|
|
958ca0530d | ||
|
|
981874a450 | ||
|
|
4502dc6fdb | ||
|
|
cc78b6193a | ||
|
|
327d64f22d | ||
|
|
c36052d477 | ||
|
|
bc9c30de56 | ||
|
|
e1c3a7d018 | ||
|
|
179aee88af | ||
|
|
df4a59c269 | ||
|
|
4d389cda63 | ||
|
|
ae56a85811 | ||
|
|
3e75bd5b95 | ||
|
|
2454afd58f | ||
|
|
7244093f21 | ||
|
|
52894e4c93 | ||
|
|
4e0b54c1a8 | ||
|
|
b5876ede0e | ||
|
|
9558fd67bb | ||
|
|
59b62a835b | ||
|
|
ad6847ec59 | ||
|
|
061c255978 | ||
|
|
bb44d0907d | ||
|
|
2215b17a54 | ||
|
|
cc52eab3f6 | ||
|
|
155a4ce50a | ||
|
|
17739377bd | ||
|
|
782fae1624 | ||
|
|
59ca1f18d6 | ||
|
|
c6cda6eb30 | ||
|
|
9f3c5d8de6 | ||
|
|
e4c9ac6ee3 | ||
|
|
5d6500523b | ||
|
|
b111ccabb5 | ||
|
|
b90a67aa43 | ||
|
|
9ed72839f4 | ||
|
|
b8f50da45f | ||
|
|
1e4dada672 | ||
|
|
a943d14807 | ||
|
|
2cf437ff34 | ||
|
|
7cbccbbb5b | ||
|
|
f79af5b6f7 | ||
|
|
05af1d04ff | ||
|
|
2753646f06 | ||
|
|
2b3889c897 | ||
|
|
e01e346dea | ||
|
|
7735037ad9 | ||
|
|
b2bcd5fc3a | ||
|
|
a40ea357db | ||
|
|
7434c22772 | ||
|
|
f0eb25b88c | ||
|
|
7ec9827db5 | ||
|
|
eeca31c92e | ||
|
|
2cd736035c | ||
|
|
bc99616e43 | ||
|
|
732f27fcf6 | ||
|
|
54e8465e47 | ||
|
|
7ceb02c001 | ||
|
|
2450a348f1 | ||
|
|
7bd469939e | ||
|
|
89180359c0 | ||
|
|
4259ace5c4 | ||
|
|
d7e5c38a62 | ||
|
|
b601105998 | ||
|
|
ca07d0716f | ||
|
|
eb16df3450 | ||
|
|
b44684760d | ||
|
|
be3fc1bb84 | ||
|
|
e781e67b43 | ||
|
|
40800ffe4b | ||
|
|
4e704e59e8 | ||
|
|
06e5d5e3d1 | ||
|
|
06010d7cd6 | ||
|
|
2549cd9a71 | ||
|
|
e89e35436e | ||
|
|
f5029ee3e9 | ||
|
|
08a84e0a8b | ||
|
|
36c819de04 | ||
|
|
f62987af4b | ||
|
|
8d9b44d6ec | ||
|
|
a5babb722d | ||
|
|
bde65f094d | ||
|
|
3fa2cfabd3 | ||
|
|
b8f4d02e4f | ||
|
|
436b7feb8e | ||
|
|
8bab4dd6ee | ||
|
|
99dfea631b | ||
|
|
16501bd6a8 | ||
|
|
88201b7308 | ||
|
|
2fb680b06a | ||
|
|
fb8116107b | ||
|
|
e6b83a1db5 | ||
|
|
607a19048b | ||
|
|
0db831e0c3 | ||
|
|
863cca507d | ||
|
|
3916a252d4 | ||
|
|
1594b76851 | ||
|
|
9e14fc5241 | ||
|
|
54fd3e2fd1 | ||
|
|
2348f8dba2 | ||
|
|
f0a3dc48f9 | ||
|
|
58ad43213b | ||
|
|
4a505b7238 | ||
|
|
7c6c0e68d2 | ||
|
|
1c5b22710d | ||
|
|
75755528b1 | ||
|
|
4dadcaf574 | ||
|
|
5a88d53443 | ||
|
|
2bd5faa7d9 | ||
|
|
29cb868f50 | ||
|
|
4eb222f5aa | ||
|
|
eca6ab2297 | ||
|
|
749e5147df | ||
|
|
55e84d0051 | ||
|
|
8cd9727429 | ||
|
|
660e839bed | ||
|
|
afb16d2045 | ||
|
|
f4b025f33c | ||
|
|
e03c3b7be0 | ||
|
|
5a2ba5f785 | ||
|
|
e1463dca05 | ||
|
|
82972127a1 | ||
|
|
272eaed351 | ||
|
|
989e67daac | ||
|
|
dcec618ae9 | ||
|
|
bc12388b6d | ||
|
|
6625488180 | ||
|
|
1209337af6 | ||
|
|
0f608b1702 | ||
|
|
cf547d517b | ||
|
|
8c75c68dca |
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@@ -54,6 +54,9 @@ 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)
|
||||
|
||||
@@ -81,14 +84,23 @@ 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/sys-clk"
|
||||
ROOT_DIR="$GITHUB_WORKSPACE/Source/hoc-clk"
|
||||
DIST_DIR="$ROOT_DIR/dist"
|
||||
|
||||
mkdir -p "$DIST_DIR"
|
||||
@@ -101,13 +113,14 @@ 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/horizon-oc.nsp" \
|
||||
"$ROOT_DIR/sysmodule/out/hoc-clk.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,9 +1,12 @@
|
||||
[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
|
||||
[submodule "Source/sys-clk/overlay/lib/libultrahand"]
|
||||
path = Source/sys-clk/overlay/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
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
Horizon OC Zeus Compilation Instructions
|
||||
Horizon OC 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 -b develop --single-branch https://github.com/Horizon-OC/Horizon-OC.git)
|
||||
5. Clone the Horizon OC develop branch (git clone https://github.com/Horizon-OC/Horizon-OC.git --recurse-submodules)
|
||||
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. 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
|
||||
9. Run ./build.sh in the root directory
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -6,6 +6,8 @@
|
||||
|
||||
- Although "sys-clk" uses permissive license, all modifications towards it in this repo ("hoc-clk") are licensed under GPL v2.
|
||||
|
||||
- Status-Monitor is licensed under the GPLv2
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
|
||||
121
README.md
121
README.md
@@ -1,7 +1,7 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
<img src="assets/logo.png" alt="logo" width="350"/>
|
||||
<img src="assets/logo.png" alt="logo" width="768"/>
|
||||
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -32,16 +32,16 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
## Default clocks
|
||||
|
||||
* **CPU:** Up to 1963MHz (Mariko) / 1785MHz (Erista)
|
||||
* **GPU:** Up to 1075MHz (Mariko) / 921MHz (Erista)
|
||||
* **RAM:** Up to 1866MHz (Mariko) / 1600MHz (Erista)
|
||||
* **RAM:** Up to 1866/2133MHz (Mariko) / 1600MHz (Erista)
|
||||
* Over/undervolting support
|
||||
* Built-in configurator
|
||||
* Compatible with most homebrew
|
||||
|
||||
> 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
|
||||
> 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
|
||||
|
||||
---
|
||||
|
||||
@@ -56,7 +56,6 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
|
||||
|
||||
```
|
||||
kip1=atmosphere/kips/hoc.kip
|
||||
secmon=exosphere.bin
|
||||
```
|
||||
|
||||
*(No changes needed if using fusee.)*
|
||||
@@ -79,22 +78,66 @@ Refer to COMPILATION.md
|
||||
---
|
||||
## Clock table
|
||||
|
||||
### MEM clocks
|
||||
### MEM clocks (mhz)
|
||||
|
||||
* 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.
|
||||
* 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.
|
||||
* 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.
|
||||
* 1331 → official handheld, JEDEC.
|
||||
* 1065
|
||||
* 800
|
||||
* 665
|
||||
|
||||
### CPU clocks
|
||||
* 2601 → mariko absolute max, very dangerous
|
||||
### CPU clocks (mhz)
|
||||
* 2703 → mariko absolute max, dangerous
|
||||
* 2601 → unsafe
|
||||
* 2499
|
||||
* 2397 → mariko safe max with UV (low speedo)
|
||||
* 2295
|
||||
@@ -115,17 +158,17 @@ Refer to COMPILATION.md
|
||||
* 714
|
||||
* 612 → sleep mode
|
||||
|
||||
### GPU clocks
|
||||
### GPU clocks (mhz)
|
||||
* 1536 → absolute max clock on mariko. very dangerous
|
||||
* 1459
|
||||
* 1382
|
||||
* 1305
|
||||
* 1267 → NVIDIA T214 rating
|
||||
* 1228 → mariko HiOPT safe clock
|
||||
* 1152 → mariko SLT max clock
|
||||
* 1075 → mariko no UV max clock. absolute max clock on erista. very dangerous
|
||||
* 998 → NVIDIA T210 rating
|
||||
* 960 (erista only) → erista slt/hiopt safe max clock
|
||||
* 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
|
||||
* 921 → erista no UV max clock
|
||||
* 844
|
||||
* 768 → official docked
|
||||
@@ -140,25 +183,33 @@ Refer to COMPILATION.md
|
||||
* 76 → boost mode
|
||||
|
||||
**Notes:**
|
||||
1. GPU overclock is capped at 460MHz in handheld and capped at 768MHz if charging, unless you're using the official charger.
|
||||
2. Clocks higher than 768MHz need the official charger is plugged in.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## Credits
|
||||
* **Lightos's Cat** - Cat
|
||||
|
||||
* **Souldbminer** – hoc-clk and loader development
|
||||
* **Lightos** – loader patches development
|
||||
* **Souldbminer** - hoc-clk and loader development
|
||||
* **Lightos** - Loader patches development, hoc-clk development, guides
|
||||
* **TDRR** - HOC Logo Design
|
||||
* **tetetete-ctrl** - Website design
|
||||
* **SciresM** - Atmosphere CFW
|
||||
* **CTCaer** - L4T, Hekate, perfect ram timings
|
||||
* **KazushiMe** – Switch OC Suite
|
||||
* **hanai3bi (meha)** – Switch OC Suite, EOS, sys-clk-eos
|
||||
* **NaGaa95** – L4T-OC-kernel
|
||||
* **B3711 (halop)** – EOS
|
||||
* **sys-clk team (m4xw, p-sam, natinusala)** – sys-clk
|
||||
* **b0rd2death** – Ultrahand sys-clk & Status Monitor fork
|
||||
* **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
|
||||
* **MasaGratoR and ZachyCatGames** - General help
|
||||
* **MasaGratoR** - Status Monitor & Display Refresh Rate Driver
|
||||
* **Dom, Samybigio, Arcdelta, Miki, Happy, Flopsider, Winnerboi77, Blaise, Alvise, TDRR, agjeococh and Xenshen** - Testing
|
||||
* **Samybigio2011** - Italian translations
|
||||
* **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
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.x | :white_check_mark: |
|
||||
| 2.x.x | :white_check_mark: |
|
||||
| 1.x.x | Not supported |
|
||||
| 0.x.x | Not supported |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __ACCESS_TABLE_NAME__ SocthermAccessTable
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceSoctherm.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_soctherm_access_table_data.inc"
|
||||
#define __ACCESS_TABLE_NAME__ RtcPmcAccessTable
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceRtcPmc.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_rtc_pmc_access_table_data.inc"
|
||||
|
||||
#include "secmon_define_access_table.inc"
|
||||
|
||||
@@ -965,3 +965,76 @@ SetRegisterAllowed(0xECC);
|
||||
SetRegisterAllowed(0xED0);
|
||||
SetRegisterAllowed(0xED4);
|
||||
SetRegisterAllowed(0xED8);
|
||||
SetRegisterAllowed(0xEDC);
|
||||
SetRegisterAllowed(0xEE0);
|
||||
SetRegisterAllowed(0xEE4);
|
||||
SetRegisterAllowed(0xEE8);
|
||||
SetRegisterAllowed(0xEEC);
|
||||
SetRegisterAllowed(0xEF0);
|
||||
SetRegisterAllowed(0xEF4);
|
||||
SetRegisterAllowed(0xEF8);
|
||||
SetRegisterAllowed(0xEFC);
|
||||
SetRegisterAllowed(0xF00);
|
||||
SetRegisterAllowed(0xF04);
|
||||
SetRegisterAllowed(0xF08);
|
||||
SetRegisterAllowed(0xF0C);
|
||||
SetRegisterAllowed(0xF10);
|
||||
SetRegisterAllowed(0xF14);
|
||||
SetRegisterAllowed(0xF18);
|
||||
SetRegisterAllowed(0xF1C);
|
||||
SetRegisterAllowed(0xF20);
|
||||
SetRegisterAllowed(0xF24);
|
||||
SetRegisterAllowed(0xF28);
|
||||
SetRegisterAllowed(0xF2C);
|
||||
SetRegisterAllowed(0xF30);
|
||||
SetRegisterAllowed(0xF34);
|
||||
SetRegisterAllowed(0xF38);
|
||||
SetRegisterAllowed(0xF3C);
|
||||
SetRegisterAllowed(0xF40);
|
||||
SetRegisterAllowed(0xF44);
|
||||
SetRegisterAllowed(0xF48);
|
||||
SetRegisterAllowed(0xF4C);
|
||||
SetRegisterAllowed(0xF50);
|
||||
SetRegisterAllowed(0xF54);
|
||||
SetRegisterAllowed(0xF58);
|
||||
SetRegisterAllowed(0xF5C);
|
||||
SetRegisterAllowed(0xF60);
|
||||
SetRegisterAllowed(0xF64);
|
||||
SetRegisterAllowed(0xF68);
|
||||
SetRegisterAllowed(0xF6C);
|
||||
SetRegisterAllowed(0xF70);
|
||||
SetRegisterAllowed(0xF74);
|
||||
SetRegisterAllowed(0xF78);
|
||||
SetRegisterAllowed(0xF7C);
|
||||
SetRegisterAllowed(0xF80);
|
||||
SetRegisterAllowed(0xF84);
|
||||
SetRegisterAllowed(0xF88);
|
||||
SetRegisterAllowed(0xF8C);
|
||||
SetRegisterAllowed(0xF90);
|
||||
SetRegisterAllowed(0xF94);
|
||||
SetRegisterAllowed(0xF98);
|
||||
SetRegisterAllowed(0xF9C);
|
||||
SetRegisterAllowed(0xFA0);
|
||||
SetRegisterAllowed(0xFA4);
|
||||
SetRegisterAllowed(0xFA8);
|
||||
SetRegisterAllowed(0xFAC);
|
||||
SetRegisterAllowed(0xFB0);
|
||||
SetRegisterAllowed(0xFB4);
|
||||
SetRegisterAllowed(0xFB8);
|
||||
SetRegisterAllowed(0xFBC);
|
||||
SetRegisterAllowed(0xFC0);
|
||||
SetRegisterAllowed(0xFC4);
|
||||
SetRegisterAllowed(0xFC8);
|
||||
SetRegisterAllowed(0xFCC);
|
||||
SetRegisterAllowed(0xFD0);
|
||||
SetRegisterAllowed(0xFD4);
|
||||
SetRegisterAllowed(0xFD8);
|
||||
SetRegisterAllowed(0xFDC);
|
||||
SetRegisterAllowed(0xFE0);
|
||||
SetRegisterAllowed(0xFE4);
|
||||
SetRegisterAllowed(0xFE8);
|
||||
SetRegisterAllowed(0xFEC);
|
||||
SetRegisterAllowed(0xFF0);
|
||||
SetRegisterAllowed(0xFF4);
|
||||
SetRegisterAllowed(0xFF8);
|
||||
SetRegisterAllowed(0xFFC);
|
||||
|
||||
@@ -129,33 +129,32 @@ namespace ams::secmon {
|
||||
constexpr inline const MemoryRegion MemoryRegionVirtualDeviceEmpty = MemoryRegion(MemoryRegionVirtualDevice.GetStartAddress(), 0);
|
||||
|
||||
#define AMS_SECMON_FOREACH_DEVICE_REGION(HANDLER, ...) \
|
||||
HANDLER(GicDistributor, Empty, UINT64_C(0x50041000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(GicCpuInterface, GicDistributor, UINT64_C(0x50042000), UINT64_C(0x2000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Uart, GicCpuInterface, UINT64_C(0x70006000), UINT64_C(0x1000), false, ## __VA_ARGS__) \
|
||||
HANDLER(ClkRst, Uart, UINT64_C(0x60006000), UINT64_C(0x1000), false, ## __VA_ARGS__) \
|
||||
HANDLER(RtcPmc, ClkRst, UINT64_C(0x7000E000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Timer, RtcPmc, UINT64_C(0x60005000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(System, Timer, UINT64_C(0x6000C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(SecurityEngine, System, UINT64_C(0x70012000), UINT64_C(0x2000), true, ## __VA_ARGS__) \
|
||||
HANDLER(SecurityEngine2, SecurityEngine, UINT64_C(0x70412000), UINT64_C(0x2000), true, ## __VA_ARGS__) \
|
||||
HANDLER(SysCtr0, SecurityEngine2, UINT64_C(0x700F0000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(MemoryController, SysCtr0, UINT64_C(0x70019000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(ExternalMemoryController, MemoryController, UINT64_C(0x7001b000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(FuseKFuse, ExternalMemoryController, UINT64_C(0x7000F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(ApbMisc, FuseKFuse, UINT64_C(0x70000000), UINT64_C(0x4000), true, ## __VA_ARGS__) \
|
||||
HANDLER(FlowController, ApbMisc, UINT64_C(0x60007000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(BootloaderParams, FlowController, UINT64_C(0x40000000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(I2c5, BootloaderParams, UINT64_C(0x7000D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Gpio, I2c5, UINT64_C(0x6000D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(I2c1, Gpio, UINT64_C(0x7000C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(ExceptionVectors, I2c1, UINT64_C(0x6000F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(MemoryController0, ExceptionVectors, UINT64_C(0x7001C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(MemoryController1, MemoryController0, UINT64_C(0x7001D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Sdmmc, MemoryController1, UINT64_C(0x700B0000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
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(GicDistributor, Empty, UINT64_C(0x50041000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(GicCpuInterface, GicDistributor, UINT64_C(0x50042000), UINT64_C(0x2000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Uart, GicCpuInterface, UINT64_C(0x70006000), UINT64_C(0x1000), false, ## __VA_ARGS__) \
|
||||
HANDLER(ClkRst, Uart, UINT64_C(0x60006000), UINT64_C(0x1000), false, ## __VA_ARGS__) \
|
||||
HANDLER(RtcPmc, ClkRst, UINT64_C(0x7000E000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Timer, RtcPmc, UINT64_C(0x60005000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(System, Timer, UINT64_C(0x6000C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(SecurityEngine, System, UINT64_C(0x70012000), UINT64_C(0x2000), true, ## __VA_ARGS__) \
|
||||
HANDLER(SecurityEngine2, SecurityEngine, UINT64_C(0x70412000), UINT64_C(0x2000), true, ## __VA_ARGS__) \
|
||||
HANDLER(SysCtr0, SecurityEngine2, UINT64_C(0x700F0000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(MemoryController, SysCtr0, UINT64_C(0x70019000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(ExternalMemoryController, MemoryController, UINT64_C(0x7001b000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(FuseKFuse, ExternalMemoryController, UINT64_C(0x7000F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(ApbMisc, FuseKFuse, UINT64_C(0x70000000), UINT64_C(0x4000), true, ## __VA_ARGS__) \
|
||||
HANDLER(FlowController, ApbMisc, UINT64_C(0x60007000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(BootloaderParams, FlowController, UINT64_C(0x40000000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(I2c5, BootloaderParams, UINT64_C(0x7000D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Gpio, I2c5, UINT64_C(0x6000D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(I2c1, Gpio, UINT64_C(0x7000C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(ExceptionVectors, I2c1, UINT64_C(0x6000F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(MemoryController0, ExceptionVectors, UINT64_C(0x7001C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(MemoryController1, MemoryController0, UINT64_C(0x7001D000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
HANDLER(Sdmmc, MemoryController1, UINT64_C(0x700B0000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||
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__) \
|
||||
|
||||
#define DEFINE_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \
|
||||
constexpr inline const MemoryRegion MemoryRegionVirtualDevice##_NAME_ = MemoryRegion(MemoryRegionVirtualDevice##_PREV_.GetEndAddress() + 0x1000, _SIZE_); \
|
||||
|
||||
268
Source/Atmosphere-Patches/secmon_smc_handler.cpp
Normal file
268
Source/Atmosphere-Patches/secmon_smc_handler.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_misc.hpp"
|
||||
#include "secmon_smc_common.hpp"
|
||||
#include "secmon_smc_handler.hpp"
|
||||
#include "secmon_smc_aes.hpp"
|
||||
#include "secmon_smc_carveout.hpp"
|
||||
#include "secmon_smc_device_unique_data.hpp"
|
||||
#include "secmon_smc_error.hpp"
|
||||
#include "secmon_smc_info.hpp"
|
||||
#include "secmon_smc_memory_access.hpp"
|
||||
#include "secmon_smc_power_management.hpp"
|
||||
#include "secmon_smc_random.hpp"
|
||||
#include "secmon_smc_register_access.hpp"
|
||||
#include "secmon_smc_result.hpp"
|
||||
#include "secmon_smc_rsa.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct HandlerInfo {
|
||||
u32 function_id;
|
||||
u32 restriction_mask;
|
||||
SmcHandler handler;
|
||||
};
|
||||
|
||||
struct HandlerTable {
|
||||
const HandlerInfo *entries;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
enum HandlerType : int {
|
||||
HandlerType_User = 0,
|
||||
HandlerType_Kern = 1,
|
||||
HandlerType_Count = 2,
|
||||
};
|
||||
|
||||
enum Restriction {
|
||||
Restriction_None = (0 << 0),
|
||||
Restriction_Normal = (1 << 0),
|
||||
Restriction_DeviceUniqueDataNotAllowed = (1 << 1),
|
||||
Restriction_SafeModeNotAllowed = (1 << 2),
|
||||
};
|
||||
|
||||
enum SmcCallRange {
|
||||
SmcCallRange_ArmArch = 0,
|
||||
SmcCallRange_Cpu = 1,
|
||||
SmcCallRange_Sip = 2,
|
||||
SmcCallRange_Oem = 3,
|
||||
SmcCallRange_Standard = 4,
|
||||
|
||||
SmcCallRange_TrustedApp = 0x30,
|
||||
};
|
||||
|
||||
enum SmcArgumentType {
|
||||
ArgumentType_Integer = 0,
|
||||
ArgumentType_Pointer = 1,
|
||||
};
|
||||
|
||||
enum SmcConvention {
|
||||
Convention_Smc32 = 0,
|
||||
Convention_Smc64 = 1,
|
||||
};
|
||||
|
||||
enum SmcCallType {
|
||||
SmcCallType_YieldingCall = 0,
|
||||
SmcCallType_FastCall = 1,
|
||||
};
|
||||
|
||||
struct SmcFunctionId {
|
||||
using FunctionId = util::BitPack64::Field< 0, 8, u32>;
|
||||
using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>;
|
||||
using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>;
|
||||
using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>;
|
||||
using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>;
|
||||
using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>;
|
||||
using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>;
|
||||
using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>;
|
||||
using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>;
|
||||
using Reserved = util::BitPack64::Field<16, 8, u32>;
|
||||
using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>;
|
||||
using Convention = util::BitPack64::Field<30, 1, SmcConvention>;
|
||||
using CallType = util::BitPack64::Field<31, 1, SmcCallType>;
|
||||
using Reserved2 = util::BitPack64::Field<32, 32, u32>;
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_user_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig },
|
||||
{ 0xC3000002, Restriction_Normal, SmcGetConfigUser },
|
||||
{ 0xC3000003, Restriction_Normal, SmcGetResult },
|
||||
{ 0xC3000404, Restriction_Normal, SmcGetResultData },
|
||||
{ 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate },
|
||||
{ 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes },
|
||||
{ 0xC3000007, Restriction_Normal, SmcGenerateAesKek },
|
||||
{ 0xC3000008, Restriction_Normal, SmcLoadAesKey },
|
||||
{ 0xC3000009, Restriction_Normal, SmcComputeAes },
|
||||
{ 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey },
|
||||
{ 0xC300040B, Restriction_Normal, SmcComputeCmac },
|
||||
{ 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData },
|
||||
{ 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData },
|
||||
{ 0xC300000E, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
|
||||
{ 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
|
||||
{ 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
|
||||
{ 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey }
|
||||
};
|
||||
|
||||
/* Deprecated handlerss. */
|
||||
constexpr inline const HandlerInfo DecryptAndImportEsDeviceKeyHandlerInfo = {
|
||||
0xC300100C, Restriction_Normal, SmcDecryptAndImportEsDeviceKey
|
||||
};
|
||||
|
||||
constexpr inline const HandlerInfo DecryptAndImportLotusKeyHandlerInfo = {
|
||||
0xC300100E, Restriction_SafeModeNotAllowed, SmcDecryptAndImportLotusKey
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_kern_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu },
|
||||
{ 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu },
|
||||
{ 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu },
|
||||
{ 0xC3000004, Restriction_Normal, SmcGetConfigKern },
|
||||
{ 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking },
|
||||
{ 0xC3000006, Restriction_Normal, SmcShowError },
|
||||
{ 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion },
|
||||
{ 0xC3000008, Restriction_Normal, SmcReadWriteRegister },
|
||||
|
||||
/* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */
|
||||
{ 0xC3000409, Restriction_Normal, SmcSetConfig },
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_ams_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xF0000201, Restriction_None, SmcIramCopy },
|
||||
{ 0xF0000002, Restriction_None, SmcReadWriteRegister },
|
||||
{ 0xF0000003, Restriction_None, SmcWriteAddress },
|
||||
{ 0xF0000404, Restriction_None, SmcGetEmummcConfig },
|
||||
{ 0xF0000005, Restriction_None, SmcShowError },
|
||||
};
|
||||
|
||||
constexpr const HandlerInfo GetSecureDataHandlerInfo = {
|
||||
0x67891234, Restriction_None, SmcGetSecureData
|
||||
};
|
||||
|
||||
constinit HandlerTable g_handler_tables[] = {
|
||||
{ g_user_handlers, util::size(g_user_handlers) },
|
||||
{ g_kern_handlers, util::size(g_kern_handlers) },
|
||||
};
|
||||
|
||||
constinit HandlerTable g_ams_handler_table = {
|
||||
g_ams_handlers, util::size(g_ams_handlers)
|
||||
};
|
||||
|
||||
NORETURN void InvalidSmcError(u64 id) {
|
||||
SetError(pkg1::ErrorInfo_UnknownSmc);
|
||||
AMS_ABORT("Invalid SMC: %lx", id);
|
||||
}
|
||||
|
||||
const HandlerTable &GetHandlerTable(HandlerType type, u64 id) {
|
||||
/* Ensure we have a valid handler type. */
|
||||
if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Provide support for legacy SmcGetSecureData. */
|
||||
if (id == GetSecureDataHandlerInfo.function_id) {
|
||||
return g_handler_tables[HandlerType_User];
|
||||
}
|
||||
|
||||
/* Check if we're a user SMC. */
|
||||
if (type == HandlerType_User) {
|
||||
/* Nintendo uses OEM SMCs. */
|
||||
/* We will assign Atmosphere extension SMCs the TrustedApplication range. */
|
||||
if (util::BitPack64{id}.Get<SmcFunctionId::CallRange>() == SmcCallRange_TrustedApp) {
|
||||
return g_ams_handler_table;
|
||||
}
|
||||
|
||||
/* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */
|
||||
if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
}
|
||||
|
||||
return g_handler_tables[type];
|
||||
}
|
||||
|
||||
const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) {
|
||||
/* Provide support for legacy SmcGetSecureData. */
|
||||
if (id == GetSecureDataHandlerInfo.function_id) {
|
||||
return GetSecureDataHandlerInfo;
|
||||
}
|
||||
|
||||
/* Get and check the index. */
|
||||
const auto index = util::BitPack64{id}.Get<SmcFunctionId::FunctionId>();
|
||||
if (AMS_UNLIKELY(index >= table.count)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Get and check the handler info. */
|
||||
const auto &handler_info = table.entries[index];
|
||||
|
||||
/* Check that the handler isn't null. */
|
||||
if (AMS_UNLIKELY(handler_info.handler == nullptr)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Check that the handler's id matches. */
|
||||
if (AMS_UNLIKELY(handler_info.function_id != id)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
return handler_info;
|
||||
}
|
||||
|
||||
bool IsHandlerRestricted(const HandlerInfo &info) {
|
||||
return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0;
|
||||
}
|
||||
|
||||
SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) {
|
||||
/* Check if the smc is restricted. */
|
||||
if (GetTargetFirmware() >= TargetFirmware_8_0_0 && AMS_UNLIKELY(IsHandlerRestricted(info))) {
|
||||
return SmcResult::NotPermitted;
|
||||
}
|
||||
|
||||
/* Invoke the smc. */
|
||||
return info.handler(args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ConfigureSmcHandlersForTargetFirmware() {
|
||||
const auto target_fw = GetTargetFirmware();
|
||||
|
||||
if (target_fw < TargetFirmware_5_0_0) {
|
||||
g_user_handlers[DecryptAndImportEsDeviceKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportEsDeviceKeyHandlerInfo;
|
||||
g_user_handlers[DecryptAndImportLotusKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportLotusKeyHandlerInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleSmc(int type, SmcArguments &args) {
|
||||
/* Get the table. */
|
||||
const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]);
|
||||
|
||||
/* Get the handler info. */
|
||||
const auto &info = GetHandlerInfo(table, args.r[0]);
|
||||
|
||||
/* Set the invocation result. */
|
||||
args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -99,16 +99,16 @@ 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_soctherm_access_table.inc"
|
||||
#include "secmon_define_rtc_pmc_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, },
|
||||
{ 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, },
|
||||
{ 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, },
|
||||
};
|
||||
|
||||
constexpr bool IsAccessAllowed(const AccessTableEntry &entry, uintptr_t address) {
|
||||
|
||||
88
Source/Atmosphere/stratosphere/loader/loader.json
Normal file
88
Source/Atmosphere/stratosphere/loader/loader.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"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,8 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
@@ -95,6 +93,8 @@ 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,7 +106,16 @@ namespace ams::ldr {
|
||||
bool has_main;
|
||||
bool has_sdk;
|
||||
bool has_subsdk;
|
||||
bool has_nso[Nso_Count];
|
||||
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. */
|
||||
@@ -116,6 +125,10 @@ 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);
|
||||
@@ -176,10 +189,15 @@ namespace ams::ldr {
|
||||
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
|
||||
}
|
||||
|
||||
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, AutoLoadModuleInfo *ali, u32 acid_flags) {
|
||||
Result LoadAutoLoadHeaders(AutoLoadModuleContext &ctx, u32 acid_flags) {
|
||||
/* Clear NSOs. */
|
||||
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
|
||||
*ali = {};
|
||||
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. */
|
||||
@@ -206,16 +224,18 @@ namespace ams::ldr {
|
||||
|
||||
/* 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());
|
||||
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:
|
||||
ali->has_rtld = true;
|
||||
ctx.rtld_idx = ctx.nso_count;
|
||||
ctx.ali.has_rtld = true;
|
||||
break;
|
||||
case Nso_Main:
|
||||
ali->has_main = true;
|
||||
ctx.main_nso_idx = ctx.nso_count;
|
||||
ctx.ali.has_main = true;
|
||||
break;
|
||||
case Nso_SubSdk0:
|
||||
case Nso_SubSdk1:
|
||||
@@ -227,64 +247,65 @@ namespace ams::ldr {
|
||||
case Nso_SubSdk7:
|
||||
case Nso_SubSdk8:
|
||||
case Nso_SubSdk9:
|
||||
ali->has_subsdk = true;
|
||||
ctx.ali.has_subsdk = true;
|
||||
break;
|
||||
case Nso_Sdk:
|
||||
ali->has_sdk = true;
|
||||
ctx.sdk_nso_idx = ctx.nso_count;
|
||||
ctx.ali.has_sdk = true;
|
||||
break;
|
||||
}
|
||||
ali->has_nso[i] = true;
|
||||
ctx.ali.nso_indices[ctx.nso_count] = static_cast<s8>(i);
|
||||
ctx.nso_count++;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckAutoLoad(const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, u32 acid_flags) {
|
||||
Result CheckAutoLoad(const AutoLoadModuleContext &ctx, u32 acid_flags) {
|
||||
/* We must always have a main. */
|
||||
R_UNLESS(ali->has_main, ldr::ResultInvalidNso());
|
||||
R_UNLESS(ctx.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());
|
||||
}
|
||||
/* Validate flags and extents for all present NSOs. */
|
||||
for (int i = 0; i < ctx.nso_count; ++i) {
|
||||
const auto &hdr = ctx.headers[i];
|
||||
|
||||
/* 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());
|
||||
/* All NSOs must not be --X. */
|
||||
/* This is "probably" not checked on Ounce? */
|
||||
R_UNLESS((hdr.flags & NsoHeader::Flag_PreventCodeReads) == 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;
|
||||
/* 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(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());
|
||||
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(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso());
|
||||
R_UNLESS(hdr.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());
|
||||
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>(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());
|
||||
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();
|
||||
@@ -300,8 +321,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 }, /* [???] */
|
||||
{ 0x010070300F50C000 }, /* [???] */
|
||||
{ 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] */
|
||||
@@ -525,7 +546,7 @@ namespace ams::ldr {
|
||||
return rand % (max + 1);
|
||||
}
|
||||
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
|
||||
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;
|
||||
@@ -536,35 +557,33 @@ namespace ams::ldr {
|
||||
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);
|
||||
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());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,11 +628,9 @@ namespace ams::ldr {
|
||||
|
||||
/* 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;
|
||||
}
|
||||
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());
|
||||
@@ -622,69 +639,88 @@ 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, 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) {
|
||||
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. */
|
||||
if (!is_compressed) {
|
||||
file_size = segment->size;
|
||||
}
|
||||
size_t file_size = is_compressed ? compressed_size : segment_size;
|
||||
|
||||
/* Validate size. */
|
||||
R_UNLESS(file_size <= segment->size, ldr::ResultInvalidNso());
|
||||
R_UNLESS(segment->size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||
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 - file_size : map_base;
|
||||
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, segment->file_offset, reinterpret_cast<void *>(load_address), file_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. */
|
||||
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_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());
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
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, nso_size, GenerateSecureRandom));
|
||||
ON_SCOPE_EXIT { os::UnmapProcessMemory(mapped_memory, process_handle, nso_address, nso_size); };
|
||||
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, 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));
|
||||
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 + 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);
|
||||
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);
|
||||
@@ -711,25 +747,31 @@ 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 NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
|
||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const AutoLoadModuleContext &ctx, 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); };
|
||||
const uintptr_t total_end = process_info->code_address + process_info->total_size;
|
||||
|
||||
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
|
||||
}
|
||||
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. */
|
||||
@@ -755,13 +797,13 @@ namespace ams::ldr {
|
||||
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) {
|
||||
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), nso_headers, ali, argument));
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), ctx, argument));
|
||||
|
||||
/* Actually create process. */
|
||||
svc::Handle process_handle;
|
||||
@@ -769,10 +811,11 @@ 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, nso_headers, ali, argument));
|
||||
R_RETURN(LoadAutoLoadModules(out, ctx, argument));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -813,13 +856,13 @@ namespace ams::ldr {
|
||||
}
|
||||
|
||||
/* 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));
|
||||
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), g_nso_headers, std::addressof(auto_load_info), argument, flags, resource_limit));
|
||||
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), ctx, argument, flags, resource_limit));
|
||||
|
||||
/* Register NSOs with the RoManager. */
|
||||
{
|
||||
@@ -831,10 +874,8 @@ 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 (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]);
|
||||
}
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
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,33 +26,63 @@
|
||||
#define DISABLED 0
|
||||
#define DEACTIVATED_GPU_FREQ 2000
|
||||
#define GPU_MIN_MIN_VOLT 480000
|
||||
#define CPU_MAX_MAX_VOLT 1235000
|
||||
#define CPU_MAX_MAX_VOLT 1200000
|
||||
|
||||
namespace ams::ldr::hoc {
|
||||
|
||||
volatile CustomizeTable C = {
|
||||
|
||||
/* Disables RAM powerdown */
|
||||
.hpMode = DISABLED,
|
||||
|
||||
.commonEmcMemVolt = 1175000, /* LPDDR4X JEDEC Specification */
|
||||
.commonEmcMemVolt = 1175000, /* LPDDR4(X) JEDEC Specification */
|
||||
.eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */
|
||||
.eristaEmcMaxClock1 = 1600000,
|
||||
.eristaEmcMaxClock2 = 1600000,
|
||||
|
||||
.marikoEmcMaxClock = 1866000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
|
||||
.marikoEmcVddqVolt = 600000, /* Micron: 600mV, other manafacturers: 640mV */
|
||||
.emcDvbShift = 0,
|
||||
/* 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,
|
||||
|
||||
// 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,
|
||||
.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,
|
||||
},
|
||||
|
||||
/* You can mix and match different latencies if needed */
|
||||
/*
|
||||
@@ -68,45 +98,43 @@ volatile CustomizeTable C = {
|
||||
* 1331WL = 12
|
||||
*/
|
||||
|
||||
.mem_burst_read_latency = RL_1866,
|
||||
.mem_burst_write_latency = WL_1866,
|
||||
.mem_burst_read_latency = RL_1600,
|
||||
.mem_burst_write_latency = WL_1600,
|
||||
|
||||
.eristaCpuUV = 0,
|
||||
.eristaCpuVmin = 800,
|
||||
.eristaCpuMaxVolt = 1200,
|
||||
/* Unlocks up to 2295 Mhz CPU, usage is not recommended. */
|
||||
/* Unlocks up to 2397 Mhz CPU, usage is not recommended. */
|
||||
.eristaCpuUnlock = DISABLED,
|
||||
|
||||
.marikoCpuUVLow = 0, // No undervolt
|
||||
.marikoCpuUVHigh = 0, // No undervolt
|
||||
|
||||
.tableConf = DEFAULT_TABLE,
|
||||
.tableConf = TBREAK_1683,
|
||||
.marikoCpuLowVmin = 620,
|
||||
.marikoCpuHighVmin = 750,
|
||||
/* 1120mV is NVIDIA rating */
|
||||
.marikoCpuMaxVolt = 1120,
|
||||
|
||||
/* Supported values: 1963000, 2091000, 2193000, 2295000, 2397000, 2499000, 2601000, 2703000. */
|
||||
/* 1963000 is official rating of T214/Mariko, fully safe. */
|
||||
/* Supported values: 1963500, 2091000, 2193000, 2295000, 2397000, 2499000, 2601000, 2703000. */
|
||||
/* 1963500 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 = 1963000,
|
||||
.marikoCpuMaxClock = 1963500,
|
||||
|
||||
.eristaCpuBoostClock = 1785000, // Default boost clock
|
||||
.marikoCpuBoostClock = 1963000, // Default boost clock
|
||||
.marikoCpuBoostClock = 1963500, // Default boost clock
|
||||
|
||||
.eristaGpuUV = 0,
|
||||
.eristaGpuVmin = 810,
|
||||
|
||||
.marikoGpuUV = 0,
|
||||
|
||||
/* 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,
|
||||
/* Vmin past 795mV won't work due boot voltage being 800mV. */
|
||||
.marikoGpuVmin = 610,
|
||||
|
||||
.marikoGpuVmax = 800,
|
||||
|
||||
@@ -199,9 +227,9 @@ volatile CustomizeTable C = {
|
||||
{ 1581000, { 1130000, }, { 2889664, -122173, 1834, } },
|
||||
{ 1683000, { 1168000, }, { 5100873, -279186, 4747, } },
|
||||
{ 1785000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||
// { 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||
// { 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
// { 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
|
||||
{ 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||
{ 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
{ 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
|
||||
},
|
||||
|
||||
.eristaCpuDvfsTableSLT = {
|
||||
@@ -223,9 +251,10 @@ volatile CustomizeTable C = {
|
||||
{ 1785000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||
{ 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
|
||||
{ 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
|
||||
{ 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
|
||||
{ 2193000, { 1256250, }, { 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!
|
||||
},
|
||||
|
||||
.marikoCpuDvfsTable = {
|
||||
|
||||
@@ -20,15 +20,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CUST_REV 1
|
||||
#define CUST_REV 3
|
||||
#define KIP_VERSION 230
|
||||
|
||||
#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,
|
||||
@@ -36,6 +35,13 @@ enum TableConfig: u32 {
|
||||
EXTREME_TABLE = 4,
|
||||
};
|
||||
|
||||
enum StepMode: u32 {
|
||||
StepMode_66MHz = 0,
|
||||
StepMode_100MHz = 1,
|
||||
StepMode_Jedec = 2,
|
||||
StepMode_133MHz = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* Read:
|
||||
* 2133RL = 40
|
||||
@@ -71,20 +77,23 @@ static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(pcv::cvb_entry_t) * pcv::D
|
||||
constexpr uint32_t ERISTA_MTC_MAGIC = 0x43544D45; // EMTC
|
||||
constexpr uint32_t MARIKO_MTC_MAGIC = 0x43544D4D; // MMTC
|
||||
|
||||
typedef struct CustomizeTable {
|
||||
struct CustomizeTable {
|
||||
u8 cust[4] = {'C', 'U', 'S', 'T'};
|
||||
u32 custRev = CUST_REV;
|
||||
u32 custRev = CUST_REV;
|
||||
u32 kipVersion = KIP_VERSION;
|
||||
|
||||
u32 placeholder;
|
||||
u32 hpMode;
|
||||
|
||||
u32 commonEmcMemVolt;
|
||||
u32 eristaEmcMaxClock;
|
||||
u32 eristaEmcMaxClock1;
|
||||
u32 eristaEmcMaxClock2;
|
||||
|
||||
StepMode stepMode;
|
||||
u32 marikoEmcMaxClock;
|
||||
u32 marikoEmcVddqVolt;
|
||||
u32 emcDvbShift;
|
||||
s32 emcDvbShift;
|
||||
u32 marikoSocVmax;
|
||||
// advanced config
|
||||
u32 t1_tRCD;
|
||||
u32 t2_tRP;
|
||||
@@ -95,6 +104,15 @@ typedef 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;
|
||||
|
||||
@@ -150,7 +168,7 @@ typedef struct CustomizeTable {
|
||||
CustomizeGpuDvfsTable marikoGpuDvfsTableSLT;
|
||||
CustomizeGpuDvfsTable marikoGpuDvfsTableHiOPT;
|
||||
|
||||
} CustomizeTable;
|
||||
};
|
||||
|
||||
extern volatile CustomizeTable C;
|
||||
|
||||
|
||||
@@ -14,15 +14,14 @@
|
||||
* 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 {
|
||||
|
||||
void CalculateTimings(double tCK_avg) {
|
||||
tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL_DBI - (C.t6_tRTW * 3) + finetRTW;
|
||||
tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL_DBI + (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_DBI)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR;
|
||||
tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW;
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../oc_common.hpp"
|
||||
#include <stratosphere.hpp>
|
||||
#include "../mtc_timing_value.hpp"
|
||||
#include "timing_tables.hpp"
|
||||
|
||||
@@ -26,8 +26,72 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fallback. */
|
||||
rext = 0x1A;
|
||||
/* > 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);
|
||||
}
|
||||
}
|
||||
|
||||
void CalculateMrw2() {
|
||||
@@ -43,7 +107,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
u32 wlIndex = 0;
|
||||
|
||||
for (u32 i = 0; i < std::size(rlMapDBI); ++i) {
|
||||
if (rlMapDBI[i] == RL_DBI) {
|
||||
if (rlMapDBI[i] == RL) {
|
||||
rlIndex = i;
|
||||
break;
|
||||
}
|
||||
@@ -56,11 +120,84 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
}
|
||||
}
|
||||
|
||||
/* DBI is always enabled. */
|
||||
mrw2 = static_cast<u8>(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6)));
|
||||
}
|
||||
|
||||
void CalculateTimings() {
|
||||
void CalculateTimings(double tCK_avg, u32 freq) {
|
||||
RL = RL_1331;
|
||||
WL = WL_1331;
|
||||
|
||||
HandleLatency(freq);
|
||||
|
||||
GetRext();
|
||||
|
||||
/* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
|
||||
/* This is a lazy workaround until I find the issue... */
|
||||
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();
|
||||
void CalculateTimings(double tCK_avg, u32 freq);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../mtc_timing_value.hpp"
|
||||
#include "../oc_common.hpp"
|
||||
#include "timing_tables.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "../mtc_timing_value.hpp"
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
|
||||
@@ -30,18 +30,13 @@ namespace ams::ldr::hoc {
|
||||
#define PACK_U32(high, low) ((static_cast<u32>(high) << 16) | (static_cast<u32>(low) & 0xFFFF))
|
||||
#define PACK_U32_NIBBLE_HIGH_BYTE_LOW(high, low) ((static_cast<u32>(high & 0xF) << 28) | (static_cast<u32>(low) & 0xFF))
|
||||
|
||||
|
||||
|
||||
/* Burst latency, not to be confused with base latency (tWRL). */
|
||||
const u32 BL = 16;
|
||||
|
||||
/* Base latency for read and write (tWRL). */
|
||||
const u32 RL = C.mem_burst_read_latency - 4; /* (This is a lazy fix for now) */
|
||||
const u32 RL = C.mem_burst_read_latency;
|
||||
const u32 WL = C.mem_burst_write_latency;
|
||||
|
||||
/* Switch uses RL_DBI, todo: get rid of non DBI_RL. */
|
||||
const u32 RL_DBI = RL + 4;
|
||||
|
||||
/* Precharge to Precharge Delay. (tCK) */
|
||||
const u32 tPPD = 4;
|
||||
|
||||
@@ -73,7 +68,7 @@ namespace ams::ldr::hoc {
|
||||
const std::array<u32, 8> tRP_values = { 18, 17, 16, 15, 14, 13, 12, 11 };
|
||||
const std::array<u32, 10> tRAS_values = { 42, 36, 34, 32, 30, 28, 26, 24, 22, 20 };
|
||||
const std::array<double, 8> tRRD_values = { 10.0, 7.5, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 };
|
||||
const std::array<u32, 11> tRFC_values = { 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40 };
|
||||
const std::array<u32, 6> tRFC_values = { 90, 80, 70, 60, 50, 40 };
|
||||
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 };
|
||||
|
||||
@@ -92,7 +87,7 @@ namespace ams::ldr::hoc {
|
||||
const u32 tFAW = static_cast<u32>(tRRD * 4.0);
|
||||
const double tRPab = tRPpb + 3;
|
||||
|
||||
const u32 tR2P = CEIL((RL_DBI * 0.426) - 2.0);
|
||||
const u32 tR2P = CEIL((RL * 0.426) - 2.0);
|
||||
inline u32 tR2W;
|
||||
inline u32 rext;
|
||||
|
||||
@@ -116,63 +111,58 @@ 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 };
|
||||
|
||||
const double tCK_avg = 1000'000.0 / C.marikoEmcMaxClock;
|
||||
inline u32 tRCD;
|
||||
inline u32 tRPpb;
|
||||
inline u32 tRAS;
|
||||
inline double tRRD;
|
||||
inline u32 tRFCpb;
|
||||
|
||||
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 tRC;
|
||||
inline u32 tRFCab;
|
||||
inline double tXSR;
|
||||
inline u32 tFAW;
|
||||
inline double tRPab;
|
||||
|
||||
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 RL;
|
||||
inline u32 WL;
|
||||
|
||||
const u32 tR2P = CEIL((RL_DBI * 0.426) - 2.0);
|
||||
const u32 tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL_DBI - (C.t6_tRTW * 3) + finetRTW;
|
||||
const u32 tRTM = FLOOR((10.0 + RL_DBI) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
|
||||
const u32 tRATM = CEIL((tRTM - 10.0) + (RL_DBI * 0.426));
|
||||
inline u32 tR2P;
|
||||
inline u32 tR2W;
|
||||
inline u32 tRTM;
|
||||
inline u32 tRATM;
|
||||
inline u32 rext;
|
||||
|
||||
const u32 rdv = RL_DBI + 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_DBI + ((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_DBI + (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_DBI)), 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 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 wdv = WL;
|
||||
const u32 wsv = WL - 2;
|
||||
const u32 wev = 0xA + (WL - 14);
|
||||
inline u32 tW2P;
|
||||
inline u32 tWTPDEN;
|
||||
inline u32 tW2R;
|
||||
inline u32 tWTM;
|
||||
inline u32 tWATM;
|
||||
|
||||
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 wdv;
|
||||
inline u32 wsv;
|
||||
inline u32 wev;
|
||||
|
||||
const u32 pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361)));
|
||||
inline u32 obdly;
|
||||
|
||||
const u32 tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0;
|
||||
inline u32 pdex2rw;
|
||||
|
||||
const double tMMRI = tRCD + (tCK_avg * 3);
|
||||
const double pdex2mrr = tMMRI + 10; /* Do this properly? */
|
||||
inline u32 tCLKSTOP;
|
||||
|
||||
inline u32 pdex2mrr;
|
||||
|
||||
inline u8 mrw2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,17 +18,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#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 <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(); }
|
||||
|
||||
#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,6 +50,9 @@ 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 {
|
||||
@@ -89,9 +92,7 @@ namespace ams::ldr::hoc {
|
||||
}
|
||||
|
||||
Result CheckResult() {
|
||||
#ifndef ATMOSPHERE_IS_STRATOSPHERE
|
||||
R_UNLESS(patched_count > 0, ldr::ResultUnsuccessfulPatcher());
|
||||
#endif
|
||||
|
||||
if (maximum_patched_count)
|
||||
R_UNLESS(patched_count <= maximum_patched_count, ldr::ResultUnsuccessfulPatcher());
|
||||
@@ -99,4 +100,25 @@ namespace ams::ldr::hoc {
|
||||
R_SUCCEED();
|
||||
}
|
||||
};
|
||||
|
||||
namespace panic {
|
||||
/* Requires modifying g_ams_handlers in secmon_smc_handler.cpp */
|
||||
constexpr inline void SmcError(u32 rgb) {
|
||||
SecmonArgs args = {};
|
||||
constexpr u32 SmcShowErrorID = 0xF0000005;
|
||||
args.X[0] = SmcShowErrorID;
|
||||
args.X[1] = rgb;
|
||||
svcCallSecureMonitor(&args);
|
||||
}
|
||||
|
||||
constexpr inline u32 PackCode(u32 r, u32 g, u32 b) {
|
||||
return ((r & 0xF) << 8) | ((g & 0xF) << 4) | ((b & 0xF) << 0);
|
||||
}
|
||||
|
||||
constexpr u32 Gpu = PackCode(0xF, 0x7, 0x0);
|
||||
constexpr u32 Cpu = PackCode(0xF, 0x0, 0x0);
|
||||
constexpr u32 Emc = PackCode(0x0, 0xF, 0xF);
|
||||
constexpr u32 Patch = PackCode(0x8, 0x0, 0xF);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
132
Source/Atmosphere/stratosphere/loader/source/oc/oc_log.cpp
Normal file
132
Source/Atmosphere/stratosphere/loader/source/oc/oc_log.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
27
Source/Atmosphere/stratosphere/loader/source/oc/oc_log.hpp
Normal file
27
Source/Atmosphere/stratosphere/loader/source/oc/oc_log.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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/ */
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ams::ldr::hoc {
|
||||
|
||||
void Log(const char *data, ...);
|
||||
void ViewLog();
|
||||
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* 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,153 +22,161 @@
|
||||
|
||||
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;
|
||||
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());
|
||||
// Double the max clk simply
|
||||
u32 max_clk = entry->freq * 2;
|
||||
entry->freq = max_clk;
|
||||
entry->vco_max = max_clk;
|
||||
R_SUCCEED();
|
||||
};
|
||||
|
||||
regulator* entry = nullptr;
|
||||
for (auto& i : entries) {
|
||||
if (R_SUCCEEDED(validator(i)))
|
||||
entry = i;
|
||||
}
|
||||
|
||||
R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry());
|
||||
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)),
|
||||
};
|
||||
|
||||
u32 emc_uv = C.commonEmcMemVolt;
|
||||
if (!emc_uv)
|
||||
R_SKIP();
|
||||
|
||||
if (emc_uv % uv_step)
|
||||
emc_uv = emc_uv / uv_step * uv_step; // rounding
|
||||
|
||||
PATCH_OFFSET(ptr, emc_uv);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SafetyCheck() {
|
||||
// if (C.custRev != CUST_REV)
|
||||
// CRASH("Triggered");
|
||||
|
||||
struct sValidator {
|
||||
volatile u32 value;
|
||||
u32 min;
|
||||
u32 max;
|
||||
bool value_required = false;
|
||||
|
||||
Result check() {
|
||||
if (!value_required && !value)
|
||||
R_SUCCEED();
|
||||
|
||||
if (min && value < min)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
if (max && value > max)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
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
|
||||
);
|
||||
|
||||
R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry());
|
||||
|
||||
u32 emc_uv = C.commonEmcMemVolt;
|
||||
if (!emc_uv) {
|
||||
R_SKIP();
|
||||
}
|
||||
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 (emc_uv % uv_step) {
|
||||
emc_uv = emc_uv / uv_step * uv_step; // rounding
|
||||
}
|
||||
|
||||
PATCH_OFFSET(ptr, emc_uv);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u32 marikoGpuDvfsMaxFreq;
|
||||
switch (C.marikoGpuUV) {
|
||||
void SafetyCheck() {
|
||||
// if (C.custRev != CUST_REV)
|
||||
// CRASH("Triggered");
|
||||
|
||||
struct sValidator {
|
||||
volatile u32 value;
|
||||
u32 min;
|
||||
u32 max;
|
||||
bool value_required = false;
|
||||
u32 panic;
|
||||
|
||||
Result check() {
|
||||
if (!value_required && !value)
|
||||
R_SUCCEED();
|
||||
|
||||
if (min && value < min)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
if (max && value > max)
|
||||
R_THROW(ldr::ResultSafetyCheckFailure());
|
||||
|
||||
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:
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
|
||||
break;
|
||||
case 1:
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq);
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq);
|
||||
break;
|
||||
case 2:
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq);
|
||||
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq);
|
||||
break;
|
||||
default:
|
||||
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
|
||||
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, 2397'000, true, panic::Cpu },
|
||||
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true, panic::Cpu },
|
||||
{ C.eristaCpuMaxVolt, 1000, 1260, false, panic::Cpu },
|
||||
{ C.marikoCpuMaxVolt, 1000, 1200, false, panic::Cpu },
|
||||
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, false, panic::Cpu },
|
||||
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000, false, panic::Cpu },
|
||||
{ C.commonEmcMemVolt, 912'500, 1350'000, false, panic::Emc }, // Official burst vmax for the RAMs is 1500mV
|
||||
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000, false, panic::Emc },
|
||||
{ C.marikoEmcMaxClock, 1600'000, 3500'000, false, panic::Emc },
|
||||
{ C.marikoEmcVddqVolt, 400'000, 750'000, false, panic::Emc },
|
||||
{ C.marikoSocVmax, 1000, 1200, false, panic::Emc },
|
||||
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, false, panic::Gpu },
|
||||
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000, false, panic::Gpu },
|
||||
{ C.marikoGpuVmax, 800, 960, false, panic::Gpu },
|
||||
};
|
||||
|
||||
for (auto &v : validators) {
|
||||
if (R_FAILED(v.check())) {
|
||||
panic::SmcError(v.panic);
|
||||
CRASH("Validation FAIL");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using namespace ams::ldr::hoc::pcv;
|
||||
sValidator validators[] = {
|
||||
{ C.eristaCpuBoostClock, 1020'000, 2295'000, true },
|
||||
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true },
|
||||
{ C.commonEmcMemVolt, 912'500, 1350'000 }, // Official burst vmax for the RAMs is 1500mV
|
||||
{ C.eristaCpuMaxVolt, 1000, 1257 },
|
||||
{ GET_MAX_OF_ARR(erista::maxClocks), 1600'000, 2600'000 },
|
||||
{ C.marikoCpuMaxVolt, 1000, 1235 },
|
||||
{ C.marikoEmcMaxClock, 1600'000, 3500'000 },
|
||||
{ C.marikoEmcVddqVolt, 250'000, 700'000 },
|
||||
{ eristaCpuDvfsMaxFreq, 1785'000, 2295'000 },
|
||||
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000 },
|
||||
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000 },
|
||||
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000 },
|
||||
};
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
SafetyCheck();
|
||||
|
||||
for (auto& i : validators) {
|
||||
if (R_FAILED(i.check()))
|
||||
CRASH("Validation FAIL");
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
if (isMariko) {
|
||||
mariko::Patch(mapped_nso, nso_size);
|
||||
} else {
|
||||
erista::Patch(mapped_nso, nso_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,271 +22,11 @@
|
||||
|
||||
#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, };
|
||||
|
||||
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 {
|
||||
const u32 maxClocks[] = { 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;
|
||||
|
||||
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 = 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]) {
|
||||
|
||||
145
Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp
Normal file
145
Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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,153 +16,151 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
namespace ams::ldr::hoc::pcv {
|
||||
#include "../mtc_timing_table.hpp"
|
||||
|
||||
typedef struct cvb_coefficients {
|
||||
s32 c0 = 0;
|
||||
s32 c1 = 0;
|
||||
s32 c2 = 0;
|
||||
s32 c3 = 0;
|
||||
s32 c4 = 0;
|
||||
s32 c5 = 0;
|
||||
} cvb_coefficients;
|
||||
namespace ams::ldr::hoc::pcv {
|
||||
|
||||
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_coefficients {
|
||||
s32 c0 = 0;
|
||||
s32 c1 = 0;
|
||||
s32 c2 = 0;
|
||||
s32 c3 = 0;
|
||||
s32 c4 = 0;
|
||||
s32 c5 = 0;
|
||||
};
|
||||
|
||||
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 cvb_entry_t {
|
||||
u64 freq;
|
||||
cvb_coefficients cvb_dfll_param;
|
||||
cvb_coefficients cvb_pll_param;
|
||||
};
|
||||
static_assert(sizeof(cvb_entry_t) == 0x38);
|
||||
|
||||
typedef struct emc_dvb_dvfs_table_t {
|
||||
u64 freq;
|
||||
s32 volt[4] = {0};
|
||||
} emc_dvb_dvfs_table_t;
|
||||
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;
|
||||
};
|
||||
|
||||
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)) 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)) 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)) 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)) 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)) 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)) 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)) 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);
|
||||
|
||||
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) {
|
||||
uint8_t* m = reinterpret_cast<uint8_t *>(entry);
|
||||
for (size_t i = 0; i < sizeof(NT); i++) {
|
||||
if (*(m + i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
auto is_empty = [](NT* entry) {
|
||||
uint8_t* m = reinterpret_cast<uint8_t *>(entry);
|
||||
for (size_t i = 0; i < sizeof(NT); i++) {
|
||||
if (*(m + i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
NT* table = const_cast<NT *>(table_head);
|
||||
size_t count = 0;
|
||||
while (count < DvfsTableEntryLimit) {
|
||||
if (is_empty(table++)) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return DvfsTableEntryLimit;
|
||||
}
|
||||
|
||||
NT* table = const_cast<NT *>(table_head);
|
||||
size_t count = 0;
|
||||
while (count < DvfsTableEntryLimit) {
|
||||
if (is_empty(table++)) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return DvfsTableEntryLimit;
|
||||
}
|
||||
template<typename T>
|
||||
T* GetDvfsTableLastEntry(T* table_head) {
|
||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||
|
||||
template<typename T>
|
||||
T* GetDvfsTableLastEntry(T* table_head) {
|
||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||
NT* table = const_cast<NT *>(table_head);
|
||||
size_t count = GetDvfsTableEntryCount(table_head);
|
||||
if (!count) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t index = count - 1;
|
||||
return table + index;
|
||||
}
|
||||
|
||||
NT* table = const_cast<NT *>(table_head);
|
||||
size_t count = GetDvfsTableEntryCount(table_head);
|
||||
if (!count) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t index = count - 1;
|
||||
return table + index;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,23 +25,23 @@
|
||||
namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
Result CpuVoltDvfs(u32 *ptr) {
|
||||
if (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 (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
|
||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||
}
|
||||
|
||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||
if (C.eristaCpuVmin) {
|
||||
PATCH_OFFSET(ptr, C.eristaCpuVmin);
|
||||
}
|
||||
|
||||
if (C.eristaCpuUV) {
|
||||
PATCH_OFFSET(ptr - 2, C.eristaCpuVmin);
|
||||
}
|
||||
|
||||
if (C.eristaCpuMaxVolt) {
|
||||
PATCH_OFFSET(ptr + 5, C.eristaCpuMaxVolt);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CpuVoltThermals(u32 *ptr) {
|
||||
@@ -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 + 9, C.eristaCpuVmin);
|
||||
PATCH_OFFSET(ptr + 6, C.eristaCpuVmin);
|
||||
}
|
||||
|
||||
if (C.eristaCpuMaxVolt) {
|
||||
@@ -67,12 +67,12 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
Result CpuVoltDfll(u32* ptr) {
|
||||
cvb_cpu_dfll_data *entry = reinterpret_cast<cvb_cpu_dfll_data *>(ptr);
|
||||
R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||
R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||
R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||
R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||
R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||
R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||
R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||
R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||
|
||||
if( !C.eristaCpuUV) {
|
||||
if (!C.eristaCpuUV) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
@@ -104,20 +104,19 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
}
|
||||
|
||||
Result GpuVoltDVFS(u32 *ptr) {
|
||||
u32 result = std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern));
|
||||
|
||||
if (result)
|
||||
if (std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern))) {
|
||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||
}
|
||||
|
||||
if (C.eristaGpuVmin)
|
||||
if (C.eristaGpuVmin) {
|
||||
PATCH_OFFSET(ptr, C.eristaGpuVmin);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GpuVoltThermals(u32 *ptr) {
|
||||
u32 result = std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern));
|
||||
if (result) {
|
||||
if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) {
|
||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||
}
|
||||
|
||||
@@ -135,7 +134,7 @@ 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, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1])))
|
||||
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1])))
|
||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||
|
||||
// Both instructions should operate on the same register
|
||||
@@ -143,6 +142,12 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
if (rd != asm_get_rd(ins2))
|
||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||
|
||||
/* Verify the limit. */
|
||||
/* TODO: Make this a little bit cleaner at some point. */
|
||||
if (AsmGetImm16(ins1) != (GpuClkOsLimit & 0xFFFF) || AsmGetImm16(ins2) != (GpuClkOsLimit >> 16)) {
|
||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||
}
|
||||
|
||||
u32 max_clock;
|
||||
switch (C.eristaGpuUV) {
|
||||
case 0:
|
||||
@@ -159,28 +164,14 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
break;
|
||||
}
|
||||
u32 asm_patch[2] = {
|
||||
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)};
|
||||
asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd),
|
||||
asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)};
|
||||
PATCH_OFFSET(ptr32, asm_patch[0]);
|
||||
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GpuFreqPllLimit(u32 *ptr) {
|
||||
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
|
||||
|
||||
// All zero except for freq
|
||||
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) {
|
||||
R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
|
||||
}
|
||||
|
||||
// Double the max clk simply
|
||||
u32 max_clk = entry->freq * 2;
|
||||
entry->freq = max_clk;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Note: This does not have proper timings, so base latency adjustment will not work. */
|
||||
/* However, it may still achieve a slightly higher frequency, but not as much as it could be. */
|
||||
/* I'm certainly not insane enough to attempt this pain again, so this will have to do *for now*. */
|
||||
@@ -225,7 +216,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
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>(0xB8)));
|
||||
WRITE_PARAM_ALL_REG(table, emc_rc, MIN(GET_CYCLE_CEIL(tRC), static_cast<u32>(0xB9)));
|
||||
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));
|
||||
@@ -285,7 +276,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
// 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_DBI;
|
||||
// table->dram_timings.rl = RL;
|
||||
|
||||
/* This needs some clean up. */
|
||||
constexpr double MC_ARB_DIV = 4.0;
|
||||
@@ -324,13 +315,13 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
|
||||
table->burst_mc_regs.mc_emem_arb_misc0 = (table->burst_mc_regs.mc_emem_arb_misc0 & 0xFFE08000) | (table->burst_mc_regs.mc_emem_arb_timing_rc + 1);
|
||||
|
||||
u32 mpcorer_ptsa_rate = MAX(static_cast<u32>(227), (table->rate_khz / 1600000) * 208);
|
||||
u32 mpcorer_ptsa_rate = MIN(static_cast<u32>(227), (table->rate_khz / 1600000) * 208);
|
||||
table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = mpcorer_ptsa_rate;
|
||||
|
||||
u32 ftop_ptsa_rate = MAX(static_cast<u32>(31), (table->rate_khz / 1600000) * 24);
|
||||
u32 ftop_ptsa_rate = MIN(static_cast<u32>(31), (table->rate_khz / 1600000) * 24);
|
||||
table->la_scale_regs.mc_ftop_ptsa_rate = ftop_ptsa_rate;
|
||||
|
||||
u32 grant_decrement = MAX(static_cast<u32>(6143), (table->rate_khz / 1600000) * 4611);
|
||||
u32 grant_decrement = MIN(static_cast<u32>(6143), (table->rate_khz / 1600000) * 4611);
|
||||
table->la_scale_regs.mc_ptsa_grant_decrement = grant_decrement;
|
||||
|
||||
constexpr u32 MaskHigh = 0xFF00FFFF;
|
||||
@@ -366,67 +357,118 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
table->dram_timings.t_rp = tRFCpb;
|
||||
table->dram_timings.t_rfc = tRFCab;
|
||||
table->emc_cfg_2 = 0x11083D;
|
||||
table->min_volt = std::min(static_cast<u32>(1050), 900 + C.emcDvbShift * 25);
|
||||
table->min_volt = (u32)std::min(static_cast<s32>(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[] = {1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800};
|
||||
u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
|
||||
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[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());
|
||||
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(maxClocks) <= EmcClkOSLimit) {
|
||||
if (GET_MAX_OF_ARR(maxEmcClocks) <= EmcClkOSLimit) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
// Make room for new mtc table, discarding useless 40.8, 68000 and 102000 MHz table
|
||||
// 40800 overwritten by 68000, ..., 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 > 2; --i) {
|
||||
std::memcpy(static_cast<void *>(table_list[i]), static_cast<void *>(table_list[i - 3]), sizeof(EristaMtcTable));
|
||||
/* 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;
|
||||
} else {
|
||||
maxEmcClocks[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < std::size(maxClocks); ++i) {
|
||||
if (maxClocks[i] > EmcClkOSLimit) {
|
||||
table_list[i]->rate_khz = maxClocks[i];
|
||||
MemMtcTableAutoAdjust(table_list[i]);
|
||||
/* 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));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MemFreqMax(u32 *ptr) {
|
||||
if (GET_MAX_OF_ARR(maxClocks) <= EmcClkOSLimit) {
|
||||
if (GET_MAX_OF_ARR(maxEmcClocks) <= EmcClkOSLimit) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
PATCH_OFFSET(ptr, GET_MAX_OF_ARR(maxClocks));
|
||||
PATCH_OFFSET(ptr, GET_MAX_OF_ARR(maxEmcClocks));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GpuFreqPllMax(u32 *ptr) {
|
||||
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
|
||||
|
||||
// All zero except for freq
|
||||
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) {
|
||||
R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
|
||||
}
|
||||
|
||||
// Double the max clk simply
|
||||
u32 max_clk = entry->freq * 2;
|
||||
entry->freq = max_clk;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// patch out 1305MHz limit on erista, don't use this!
|
||||
// Result GpuFreqPllLimit(u32 *ptr) {
|
||||
// u32 prev_freq = *(ptr - 1);
|
||||
|
||||
// if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) {
|
||||
// R_THROW(ldr::ResultInvalidGpuPllEntry());
|
||||
// }
|
||||
|
||||
// PATCH_OFFSET(ptr, 3600000);
|
||||
|
||||
// R_SUCCEED();
|
||||
// }
|
||||
|
||||
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, static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq)},
|
||||
{"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, 825},
|
||||
{"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 Freq PLL", &GpuFreqPllLimit, 1, 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, 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 },
|
||||
};
|
||||
|
||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) {
|
||||
@@ -441,8 +483,11 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
constexpr 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);
|
||||
|
||||
}
|
||||
@@ -18,52 +18,13 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include "pcv.hpp"
|
||||
#include "../mtc_timing_value.hpp"
|
||||
#include "../mariko/calculate_timings.hpp"
|
||||
|
||||
namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
u32 GetGpuVminVoltage() {
|
||||
for (auto e : vminTable) {
|
||||
if (C.gpuSpeedo <= e.speedo) {
|
||||
return e.voltage;
|
||||
}
|
||||
}
|
||||
|
||||
return 530;
|
||||
}
|
||||
|
||||
u32 GetRamVminAdjustment(u32 vmin) {
|
||||
if (C.marikoEmcMaxClock < 2133000) {
|
||||
return vmin;
|
||||
}
|
||||
|
||||
const u32 ramScale = (((C.marikoEmcMaxClock / 1000) - 2133) / 33) * 5 + vmin;
|
||||
|
||||
for (auto r : ramOffset) {
|
||||
if (C.marikoEmcMaxClock < r.maxClock) {
|
||||
return ramScale + r.offset;
|
||||
}
|
||||
}
|
||||
|
||||
return ramScale;
|
||||
}
|
||||
|
||||
/* Note: EOS (probably?) has a bug in this function that always results in high vmin, this is fixed here. */
|
||||
u32 GetAutoVoltage() {
|
||||
u32 voltage = GetGpuVminVoltage();
|
||||
voltage = GetRamVminAdjustment(voltage);
|
||||
|
||||
u32 voltageOffset = 590 - C.commonGpuVoltOffset;
|
||||
|
||||
if (voltageOffset < voltage) {
|
||||
voltage = voltageOffset;
|
||||
}
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
||||
Result GpuVoltDVFS(u32 *ptr) {
|
||||
/* Check for valid pattern. */
|
||||
for (size_t i = 0; i < std::size(gpuDVFSPattern); ++i) {
|
||||
@@ -77,15 +38,10 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
PATCH_OFFSET(ptr + 1, C.marikoGpuVmax);
|
||||
}
|
||||
|
||||
/* C.marikoGpuVmin is non zero, user sets manual voltage. */
|
||||
if (C.marikoGpuVmin) {
|
||||
PATCH_OFFSET(ptr, C.marikoGpuVmin);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* C.marikoGpuVmin is zero, auto voltage is applied. */
|
||||
u32 autoVmin = GetAutoVoltage();
|
||||
PATCH_OFFSET(ptr, autoVmin);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -94,24 +50,15 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||
}
|
||||
|
||||
u32 vmin = C.marikoGpuVmin;
|
||||
|
||||
/* Automatic voltage. */
|
||||
if (!C.marikoGpuVmin) {
|
||||
vmin = GetAutoVoltage();
|
||||
PATCH_OFFSET(ptr, vmin);
|
||||
PATCH_OFFSET(ptr + 3, vmin);
|
||||
PATCH_OFFSET(ptr + 6, vmin);
|
||||
PATCH_OFFSET(ptr + 9, vmin);
|
||||
} else {
|
||||
/* Manual voltage. */
|
||||
PATCH_OFFSET(ptr, vmin);
|
||||
PATCH_OFFSET(ptr + 3, vmin);
|
||||
PATCH_OFFSET(ptr + 6, vmin);
|
||||
PATCH_OFFSET(ptr + 9, vmin);
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
PATCH_OFFSET(ptr + 12, vmin);
|
||||
PATCH_OFFSET(ptr, C.marikoGpuVmin);
|
||||
PATCH_OFFSET(ptr + 3, C.marikoGpuVmin);
|
||||
PATCH_OFFSET(ptr + 6, C.marikoGpuVmin);
|
||||
PATCH_OFFSET(ptr + 9, C.marikoGpuVmin);
|
||||
PATCH_OFFSET(ptr + 12, C.marikoGpuVmin);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -132,16 +79,17 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
Result CpuFreqVdd(u32 *ptr) {
|
||||
dvfs_rail *entry = reinterpret_cast<dvfs_rail *>(reinterpret_cast<u8 *>(ptr) - offsetof(dvfs_rail, freq));
|
||||
|
||||
R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry());
|
||||
R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||
R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||
R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||
R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry());
|
||||
R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||
R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||
R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||
|
||||
if (C.marikoCpuUVHigh) {
|
||||
PATCH_OFFSET(ptr, CapCpuClock());
|
||||
} else {
|
||||
PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -266,7 +214,6 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch (C.marikoCpuUVHigh) {
|
||||
case 1:
|
||||
PATCH_OFFSET(&(entry->tune1_high), 0);
|
||||
@@ -323,13 +270,15 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
Result GpuFreqMaxAsm(u32 *ptr32) {
|
||||
// Check if both two instructions match the pattern
|
||||
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
|
||||
if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1])))
|
||||
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) {
|
||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||
}
|
||||
|
||||
// Both instructions should operate on the same register
|
||||
u8 rd = asm_get_rd(ins1);
|
||||
if (rd != asm_get_rd(ins2))
|
||||
if (rd != asm_get_rd(ins2)) {
|
||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||
}
|
||||
|
||||
u32 max_clock;
|
||||
switch (C.marikoGpuUV) {
|
||||
@@ -346,9 +295,10 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 asm_patch[2] = {
|
||||
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)
|
||||
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]);
|
||||
@@ -395,6 +345,8 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
TABLE->shadow_regs_ca_train.PARAM = VALUE; \
|
||||
TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
|
||||
|
||||
const double tCK_avg = 1000'000.0 / table->rate_khz;
|
||||
|
||||
#define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg))
|
||||
|
||||
/* Ram power down */
|
||||
@@ -416,11 +368,13 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
u32 trefbw = refresh_raw + 0x40;
|
||||
trefbw = MIN(trefbw, static_cast<u32>(0x3FFF));
|
||||
|
||||
CalculateTimings();
|
||||
const u32 dyn_self_ref_control = (static_cast<u32>(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000);
|
||||
|
||||
CalculateTimings(tCK_avg, table->rate_khz);
|
||||
|
||||
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>(0xB8)));
|
||||
WRITE_PARAM_ALL_REG(table, emc_rc, MIN(GET_CYCLE_CEIL(tRC), static_cast<u32>(0xB9)));
|
||||
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));
|
||||
@@ -443,11 +397,10 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
WRITE_PARAM_ALL_REG(table, emc_twtm, tWTM);
|
||||
WRITE_PARAM_ALL_REG(table, emc_twatm, tWATM);
|
||||
WRITE_PARAM_ALL_REG(table, emc_rext, rext);
|
||||
WRITE_PARAM_ALL_REG(table, emc_wext, (C.marikoEmcMaxClock >= 2533000) ? 0x19 : 0x16);
|
||||
WRITE_PARAM_ALL_REG(table, emc_wext, (table->rate_khz >= 2533000) ? 0x19 : 0x16);
|
||||
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);
|
||||
@@ -480,26 +433,26 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, rdv);
|
||||
WRITE_PARAM_ALL_REG(table, emc_rdv_mask, rdv + 2);
|
||||
WRITE_PARAM_ALL_REG(table, emc_tr_rdv, rdv);
|
||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_2, 0x24)
|
||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_3, 0x24)
|
||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_2, 0x24);
|
||||
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_3, 0x24);
|
||||
|
||||
/* This needs some clean up. */
|
||||
constexpr double MC_ARB_DIV = 4.0;
|
||||
constexpr u32 MC_ARB_SFA = 2;
|
||||
constexpr u32 MC_ARB_SFA = 2;
|
||||
|
||||
table->burst_mc_regs.mc_emem_arb_cfg = C.marikoEmcMaxClock / (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;
|
||||
|
||||
/* Two consecutive reads between two different dram modules. */
|
||||
/* Only be above 1 for 8gb ram. */
|
||||
/* Only above 1 for 8gb ram. */
|
||||
if (table->burst_mc_regs.mc_emem_arb_timing_r2r > 1) {
|
||||
table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / 4) - 1 + MC_ARB_SFA;
|
||||
}
|
||||
@@ -529,7 +482,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = 0x115;
|
||||
|
||||
if (C.marikoEmcMaxClock >= 2133000) {
|
||||
if (table->rate_khz >= 2133000) {
|
||||
table->la_scale_regs.mc_ftop_ptsa_rate = 0x1F;
|
||||
} else {
|
||||
table->la_scale_regs.mc_ftop_ptsa_rate = 0x1B;
|
||||
@@ -538,14 +491,14 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
table->la_scale_regs.mc_ptsa_grant_decrement = 0x17ff;
|
||||
|
||||
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 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
|
||||
const u32 allowance2 = static_cast<u32>(0x9C40 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
|
||||
const u32 allowance3 = static_cast<u32>(0xB540 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
|
||||
const u32 allowance4 = static_cast<u32>(0x9600 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
|
||||
const u32 allowance5 = static_cast<u32>(0x8980 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
|
||||
const u32 allowance1 = static_cast<u32>(0x32000 / (table->rate_khz / 1000)) & 0xFF;
|
||||
const u32 allowance2 = static_cast<u32>(0x9C40 / (table->rate_khz / 1000)) & 0xFF;
|
||||
const u32 allowance3 = static_cast<u32>(0xB540 / (table->rate_khz / 1000)) & 0xFF;
|
||||
const u32 allowance4 = static_cast<u32>(0x9600 / (table->rate_khz / 1000)) & 0xFF;
|
||||
const u32 allowance5 = static_cast<u32>(0x8980 / (table->rate_khz / 1000)) & 0xFF;
|
||||
|
||||
table->la_scale_regs.mc_latency_allowance_xusb_0 = (table->la_scale_regs.mc_latency_allowance_xusb_0 & MaskHigh) | (allowance1 << 16);
|
||||
table->la_scale_regs.mc_latency_allowance_xusb_1 = (table->la_scale_regs.mc_latency_allowance_xusb_1 & MaskHigh) | (allowance1 << 16);
|
||||
@@ -567,153 +520,332 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
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->dram_timings.rl = RL_DBI;
|
||||
|
||||
table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast<u32>(mrw2);
|
||||
table->emc_cfg_2 = 0x11083D;
|
||||
table->dram_timings.rl = RL;
|
||||
table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast<u32>(mrw2);
|
||||
table->emc_cfg_2 = 0x11083D;
|
||||
}
|
||||
|
||||
// WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE(10.0));
|
||||
// WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE(10.0));
|
||||
// WRITE_PARAM_ALL_REG(table, emc_pchg2pden, GET_CYCLE(1.75));
|
||||
// WRITE_PARAM_ALL_REG(table, emc_ar2pden, GET_CYCLE(1.75));
|
||||
// WRITE_PARAM_ALL_REG(table, emc_pdex2cke, GET_CYCLE(1.75));
|
||||
// WRITE_PARAM_ALL_REG(table, emc_act2pden, GET_CYCLE(14.0));
|
||||
// WRITE_PARAM_ALL_REG(table, emc_cke2pden, GET_CYCLE(5.0));
|
||||
|
||||
void MemMtcPllmbDivisor(MarikoMtcTable *table) {
|
||||
constexpr u32 PllOscInKHz = 38400;
|
||||
constexpr u32 PllOscHalfKHz = 19200;
|
||||
|
||||
double target_freq_d = static_cast<double>(C.marikoEmcMaxClock);
|
||||
double target_freq_d = static_cast<double>(table->rate_khz);
|
||||
|
||||
s32 divm_candidate_half = static_cast<u8>(C.marikoEmcMaxClock / PllOscHalfKHz);
|
||||
s32 divm_candidate_half = static_cast<u8>(table->rate_khz / PllOscHalfKHz);
|
||||
|
||||
bool remainder_check = (C.marikoEmcMaxClock - PllOscInKHz * (C.marikoEmcMaxClock / PllOscInKHz)) > (C.marikoEmcMaxClock - PllOscHalfKHz * divm_candidate_half) && static_cast<int>(((target_freq_d / PllOscHalfKHz - divm_candidate_half - 0.5) * 8192.0)) != 0;
|
||||
bool remainder_check = (table->rate_khz - PllOscInKHz * (table->rate_khz / PllOscInKHz)) > (table->rate_khz - PllOscHalfKHz * divm_candidate_half) && static_cast<int>(((target_freq_d / PllOscHalfKHz - divm_candidate_half - 0.5) * 8192.0)) != 0;
|
||||
|
||||
u32 divm_final = remainder_check + 1;
|
||||
u32 divm_final = remainder_check + 1;
|
||||
table->pllmb_divm = divm_final;
|
||||
|
||||
double div_step_d = static_cast<double>(PllOscInKHz) / divm_final;
|
||||
s32 divn_integer = static_cast<u8>(C.marikoEmcMaxClock / div_step_d);
|
||||
s32 divn_integer = static_cast<u8>(table->rate_khz / div_step_d);
|
||||
table->pllmb_divn = divn_integer;
|
||||
|
||||
u32 divn_fraction = static_cast<s32>((target_freq_d / div_step_d - divn_integer - 0.5) * 8192.0);
|
||||
|
||||
u32 actual_freq_khz = static_cast<u32>((divn_integer + 0.5 + divn_fraction * 0.000122070312) * div_step_d);
|
||||
|
||||
if (C.marikoEmcMaxClock - 2366001 < 133999) {
|
||||
if (table->rate_khz - 2366001 < 133999) {
|
||||
s32 divn_fraction_ssc = static_cast<s32>((actual_freq_khz * 0.997 / div_step_d - divn_integer - 0.5) * 8192.0);
|
||||
|
||||
double delta_scaled = (0.3 / div_step_d + 0.3 / div_step_d) * (divn_fraction - divn_fraction_ssc);
|
||||
s32 delta_int = static_cast<s32>(delta_scaled);
|
||||
double delta_frac = delta_scaled - delta_int;
|
||||
s32 delta_int = static_cast<s32>(delta_scaled);
|
||||
double delta_frac = delta_scaled - delta_int;
|
||||
|
||||
u32 setup_value = 0;
|
||||
if (delta_frac <= 0.5) {
|
||||
double round_val = (delta_int + ROUND(delta_frac + delta_frac)) ? 0.5 : 0.0;
|
||||
setup_value = ROUND(delta_frac + delta_frac) ? static_cast<u32>(round_val + round_val) | 0x1000 : static_cast<u32>(round_val);
|
||||
setup_value = ROUND(delta_frac + delta_frac) ? static_cast<u32>(round_val + round_val) | 0x1000 : static_cast<u32>(round_val);
|
||||
} else {
|
||||
s32 frac_doubled = ROUND(delta_frac - 0.5 + delta_frac - 0.5);
|
||||
double round_val = 1.0;
|
||||
setup_value = frac_doubled ? static_cast<u32>(round_val) : static_cast<u32>(round_val + round_val) | 0x1000;
|
||||
setup_value = frac_doubled ? static_cast<u32>(round_val) : static_cast<u32>(round_val + round_val) | 0x1000;
|
||||
}
|
||||
|
||||
u32 ctrl1 = static_cast<u16>(divn_fraction_ssc) | (static_cast<u16>(divn_fraction) << 16);
|
||||
u32 ctrl2 = static_cast<u16>(divn_fraction) | (static_cast<u16>(setup_value) << 16);
|
||||
u32 ctrl2 = static_cast<u16>(divn_fraction) | (static_cast<u16>(setup_value) << 16);
|
||||
|
||||
table->pllm_ss_ctrl1 = ctrl1;
|
||||
table->pllm_ss_ctrl2 = ctrl2;
|
||||
table->pllm_ss_ctrl1 = ctrl1;
|
||||
table->pllm_ss_ctrl2 = ctrl2;
|
||||
table->pllmb_ss_ctrl1 = ctrl1;
|
||||
table->pllmb_ss_ctrl2 = ctrl2;
|
||||
} else {
|
||||
table->pllm_ss_cfg &= 0xBFFFFFFF;
|
||||
table->pllm_ss_cfg &= 0xBFFFFFFF;
|
||||
table->pllmb_ss_cfg &= 0xBFFFFFFF;
|
||||
|
||||
u64 pair = (static_cast<u64>(divn_fraction) << 32) | static_cast<u64>(C.marikoEmcMaxClock);
|
||||
u32 pll_misc = (table->pllm_ss_ctrl2 & 0xFFFF0000) | static_cast<u32>((pair - actual_freq_khz) >> 32);
|
||||
u64 pair = (static_cast<u64>(divn_fraction) << 32) | static_cast<u64>(table->rate_khz);
|
||||
u32 pll_misc = (table->pllm_ss_ctrl2 & 0xFFFF0000) | static_cast<u32>((pair - actual_freq_khz) >> 32);
|
||||
|
||||
table->pllm_ss_ctrl2 = pll_misc;
|
||||
table->pllm_ss_ctrl2 = pll_misc;
|
||||
table->pllmb_ss_ctrl2 = pll_misc;
|
||||
}
|
||||
}
|
||||
|
||||
Result MemFreqMtcTable(u32 *ptr) {
|
||||
u32 khz_list[] = {1600000, 1331200, 204000};
|
||||
u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
|
||||
namespace {
|
||||
std::vector<u32> newEmcList;
|
||||
u32 *nsoStart;
|
||||
}
|
||||
|
||||
// Generate list for mtc table pointers
|
||||
MarikoMtcTable *table_list[khz_list_size];
|
||||
for (u32 i = 0; i < khz_list_size; i++) {
|
||||
u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(MarikoMtcTable, rate_khz) - i * sizeof(MarikoMtcTable);
|
||||
table_list[i] = reinterpret_cast<MarikoMtcTable *>(table);
|
||||
R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
|
||||
R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
|
||||
void MtcGenerateJedecTable() {
|
||||
const u32 jedecFreqs[] = { 1866000, 1996000, 2133000, 2400000, 2666000, 2933000, 3200000 };
|
||||
constexpr u32 JedecFreqCount = std::size(jedecFreqs);
|
||||
|
||||
for (u32 i = 0; i < JedecFreqCount; ++i) {
|
||||
if (jedecFreqs[i] <= C.marikoEmcMaxClock) {
|
||||
newEmcList.push_back(jedecFreqs[i]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (C.marikoEmcMaxClock <= EmcClkOSLimit)
|
||||
if (newEmcList.back() != C.marikoEmcMaxClock) {
|
||||
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
|
||||
}
|
||||
|
||||
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
|
||||
}
|
||||
|
||||
void MtcGenerate133StepTable() {
|
||||
const u32 stepFreqs133[] = { 1733000, 1866000, 2000000, 2133000, 2266000, 2400000, 2533000, 2666000, 2800000, 2933000, 3066000, 3200000, 3333000, 3466000, }; // Avoid rounding issues
|
||||
constexpr u32 StepFreqs133Size = std::size(stepFreqs133);
|
||||
|
||||
for (u32 i = 0; i < StepFreqs133Size; ++i) {
|
||||
if (stepFreqs133[i] <= C.marikoEmcMaxClock) {
|
||||
newEmcList.push_back(stepFreqs133[i]);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newEmcList.back() != C.marikoEmcMaxClock) {
|
||||
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
|
||||
}
|
||||
|
||||
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
|
||||
}
|
||||
|
||||
void MtcGenerateFreqTables() {
|
||||
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
newEmcList.clear();
|
||||
newEmcList.reserve(DvfsTableEntryCount);
|
||||
newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault));
|
||||
|
||||
u32 stepRate = 0;
|
||||
switch (C.stepMode) {
|
||||
case StepMode_66MHz:
|
||||
stepRate = 66667;
|
||||
break;
|
||||
case StepMode_100MHz:
|
||||
stepRate = 100000;
|
||||
break;
|
||||
case StepMode_Jedec:
|
||||
MtcGenerateJedecTable();
|
||||
return;
|
||||
case StepMode_133MHz:
|
||||
MtcGenerate133StepTable();
|
||||
return;
|
||||
default:
|
||||
stepRate = 66667;
|
||||
break;
|
||||
}
|
||||
|
||||
constexpr u32 RoundHz = 1000;
|
||||
for (u32 stepIndex = 1;; ++stepIndex) {
|
||||
u32 newFreq = EmcClkOSLimit + stepIndex * stepRate;
|
||||
newFreq = (newFreq / RoundHz) * RoundHz;
|
||||
if (newFreq > C.marikoEmcMaxClock) {
|
||||
if (newEmcList.back() != C.marikoEmcMaxClock) {
|
||||
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
|
||||
}
|
||||
break;
|
||||
}
|
||||
newEmcList.push_back(newFreq);
|
||||
}
|
||||
|
||||
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
|
||||
}
|
||||
|
||||
Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) {
|
||||
R_UNLESS(tableStart->rate_khz == expectedFreq, ldr::ResultInvalidMtcTable());
|
||||
R_UNLESS(tableStart->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MtcValidateAllTables(MarikoMtcTable *tableStart, const u32 *validationList, u32 tableCount) {
|
||||
for (u32 i = 0; i < tableCount; ++i) {
|
||||
R_TRY(VerifyMtcTable(&tableStart[i], validationList[i]));
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
DramId GetDramId() {
|
||||
u64 id64;
|
||||
splGetConfig(SplConfigItem_DramId, &id64);
|
||||
return static_cast<DramId>(id64);
|
||||
}
|
||||
|
||||
MtcTableIndex GetMtcDramIndex(DramId dramId) {
|
||||
for (u32 i = 0; i < std::size(mtcIndexTable); ++i) {
|
||||
if (mtcIndexTable[i].dramId == dramId) {
|
||||
return mtcIndexTable[i].index;
|
||||
}
|
||||
}
|
||||
|
||||
return MtcTableIndex_Invalid;
|
||||
}
|
||||
|
||||
NORETURN void AbortInvalidMtc(const char *crashMsg) {
|
||||
panic::SmcError(panic::Emc);
|
||||
CRASH(crashMsg);
|
||||
}
|
||||
|
||||
u32 GetMtcOffset(MtcTableIndex index) {
|
||||
if (index < T210b0SdevEmcDvfsTableS4gb03) {
|
||||
return index * mariko::MtcFullTableSize;
|
||||
}
|
||||
|
||||
/* There are 2 erista mtc tables between T210b0SdevEmcDvfsTableS4gb01 and T210b0SdevEmcDvfsTableS4gb03, so we have to do this adjustment. */
|
||||
return mariko::MtcFullTableSize * index + (2 * erista::MtcFullTableSize);
|
||||
}
|
||||
|
||||
void PrepareMtcMemoryRegion(u8 *firstTable, MarikoMtcTable *usedTable) {
|
||||
memmove(firstTable, usedTable, mariko::MtcFullTableSize);
|
||||
|
||||
/* Clear all other tables. */
|
||||
/* 1 erista table is excluded because it's always before firstTable. */
|
||||
/* We also exclude the used table obviously. */
|
||||
constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * (mariko::MtcFullTableCount - 1) + (erista::MtcFullTableSize * (erista::MtcFullTableCount - 1));
|
||||
memset(firstTable + mariko::MtcFullTableSize, 0, RemainingRegionSize);
|
||||
}
|
||||
|
||||
void MtcExtendTables(MarikoMtcTable *table) {
|
||||
for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) {
|
||||
std::memcpy(&table[i], &table[i - 1], sizeof(MarikoMtcTable));
|
||||
table[i].rate_khz = newEmcList[i];
|
||||
}
|
||||
}
|
||||
|
||||
Result MemFreqMtcTable(u32 *ptr) {
|
||||
static const DramId dramId = [] {
|
||||
DramId id = GetDramId();
|
||||
return id;
|
||||
}();
|
||||
|
||||
static const MtcTableIndex mtcIndex = [] {
|
||||
MtcTableIndex idx = GetMtcDramIndex(dramId);
|
||||
/* If for some reason this happens, there is no chance of recovering this. */
|
||||
if (idx == MtcTableIndex_Invalid) {
|
||||
AbortInvalidMtc("Invalid dramId");
|
||||
}
|
||||
return idx;
|
||||
}();
|
||||
|
||||
/* Offset to dram id specific mtc table. */
|
||||
static const u32 mtcOffset = GetMtcOffset(mtcIndex);
|
||||
|
||||
/* Offset from 1600MHz pointer to 204Mhz table start. */
|
||||
constexpr u32 StartAdjustment = offsetof(MarikoMtcTable, rate_khz) + sizeof(MarikoMtcTable) * (mariko::MtcTableCountDefault - 1);
|
||||
u8 *startPtr = reinterpret_cast<u8 *>(ptr) - StartAdjustment;
|
||||
MarikoMtcTable *table = reinterpret_cast<MarikoMtcTable *>(startPtr + mtcOffset);
|
||||
R_TRY(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault));
|
||||
|
||||
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
MarikoMtcTable *table_alt = table_list[1], *table_max = table_list[0];
|
||||
MarikoMtcTable *tmp = new MarikoMtcTable;
|
||||
PrepareMtcMemoryRegion(startPtr, table);
|
||||
table = reinterpret_cast<MarikoMtcTable *>(startPtr);
|
||||
|
||||
// Copy unmodified 1600000 table to tmp
|
||||
std::memcpy(reinterpret_cast<void *>(tmp), reinterpret_cast<void *>(table_max), sizeof(MarikoMtcTable));
|
||||
// Adjust max freq mtc timing parameters with reference to 1331200 table
|
||||
/* TODO: Implement mariko */
|
||||
if (R_FAILED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault))) {
|
||||
AbortInvalidMtc("Failed mtc validation");
|
||||
}
|
||||
|
||||
MemMtcTableAutoAdjust(table_max);
|
||||
MtcExtendTables(table);
|
||||
|
||||
MemMtcPllmbDivisor(table_max);
|
||||
// Overwrite 13312000 table with unmodified 1600000 table copied back
|
||||
std::memcpy(reinterpret_cast<void *>(table_alt), reinterpret_cast<void *>(tmp), sizeof(MarikoMtcTable));
|
||||
if (R_FAILED(MtcValidateAllTables(table, newEmcList.data(), newEmcList.size()))) {
|
||||
AbortInvalidMtc("Failed mtc validation");
|
||||
}
|
||||
|
||||
delete tmp;
|
||||
for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) {
|
||||
MemMtcTableAutoAdjust(&table[i]);
|
||||
MemMtcPllmbDivisor(&table[i]);
|
||||
}
|
||||
|
||||
PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MemFreqDvbTable(u32 *ptr) {
|
||||
emc_dvb_dvfs_table_t *default_end = reinterpret_cast<emc_dvb_dvfs_table_t *>(ptr);
|
||||
emc_dvb_dvfs_table_t *new_start = default_end + 1;
|
||||
DvbEntry *default_end = reinterpret_cast<DvbEntry *>(ptr);
|
||||
DvbEntry *new_start = default_end + 1;
|
||||
|
||||
// Validate existing table
|
||||
void *mem_dvb_table_head = reinterpret_cast<u8 *>(new_start) - sizeof(EmcDvbTableDefault);
|
||||
bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0;
|
||||
bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0;
|
||||
R_UNLESS(validated, ldr::ResultInvalidDvbTable());
|
||||
|
||||
if (C.marikoEmcMaxClock <= EmcClkOSLimit)
|
||||
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
|
||||
R_SKIP();
|
||||
|
||||
int32_t voltAdd = 25 * C.emcDvbShift;
|
||||
|
||||
#define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000),
|
||||
|
||||
if (C.marikoEmcMaxClock < 1862400) {
|
||||
std::memcpy(new_start, default_end, sizeof(emc_dvb_dvfs_table_t));
|
||||
} else if (C.marikoEmcMaxClock < 2131200) {
|
||||
emc_dvb_dvfs_table_t oc_table = {1862400, {700, 675, 650, }};
|
||||
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
|
||||
} else if (C.marikoEmcMaxClock < 2400000) {
|
||||
emc_dvb_dvfs_table_t oc_table = {2131200, { 725, 700, 675} };
|
||||
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
|
||||
} else if (C.marikoEmcMaxClock < 2665600) {
|
||||
emc_dvb_dvfs_table_t oc_table = {2400000, {DVB_VOLT(750, 725, 700)}};
|
||||
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
|
||||
} else if (C.marikoEmcMaxClock < 2931200) {
|
||||
emc_dvb_dvfs_table_t oc_table = {2665600, {DVB_VOLT(775, 750, 725)}};
|
||||
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
|
||||
} else if (C.marikoEmcMaxClock < 3200000) {
|
||||
emc_dvb_dvfs_table_t oc_table = {2931200, {DVB_VOLT(800, 775, 750)}};
|
||||
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
|
||||
} else {
|
||||
emc_dvb_dvfs_table_t oc_table = {3200000, {DVB_VOLT(800, 800, 775)}};
|
||||
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
|
||||
}
|
||||
new_start->freq = C.marikoEmcMaxClock;
|
||||
|
||||
u32 max0 = 1050;
|
||||
u32 max1 = 1025;
|
||||
u32 max2 = 1000;
|
||||
s32 voltAdd = 25 * C.emcDvbShift;
|
||||
|
||||
if (C.marikoSocVmax && C.marikoSocVmax > 1000) {
|
||||
max0 = C.marikoSocVmax;
|
||||
max1 = C.marikoSocVmax;
|
||||
max2 = C.marikoSocVmax;
|
||||
}
|
||||
|
||||
auto DvbVolt = [&](u32 zero, u32 one, u32 two) {
|
||||
return std::array<u32, 3>{
|
||||
std::min((u32)((s32)zero + voltAdd), max0),
|
||||
std::min((u32)((s32)one + voltAdd), max1),
|
||||
std::min((u32)((s32)two + voltAdd), max2)
|
||||
};
|
||||
};
|
||||
|
||||
#define DVB(v) \
|
||||
static_cast<u32>((v)[0]), \
|
||||
static_cast<u32>((v)[1]), \
|
||||
static_cast<u32>((v)[2])
|
||||
DvbEntry emcDvbTableNew[] = {
|
||||
{ 204000, { 637, 637, 637, }, },
|
||||
{ 1331200, { 650, 637, 637, }, },
|
||||
{ 1600000, { 675, 650, 637, }, },
|
||||
{ 1866000, { DVB(DvbVolt( 700, 675, 650)) }, },
|
||||
{ 2133000, { DVB(DvbVolt( 725, 700, 675)) }, },
|
||||
{ 2400000, { DVB(DvbVolt( 750, 725, 700)) }, },
|
||||
{ 2666000, { DVB(DvbVolt( 850, 825, 800)) }, },
|
||||
{ 2933000, { DVB(DvbVolt( 950, 925, 900)) }, },
|
||||
{ 3200000, { DVB(DvbVolt(1050, 1025, 1000)) }, },
|
||||
{ 0xFFFFFFFF, { }, },
|
||||
};
|
||||
#undef DVB
|
||||
|
||||
u32 j = MtcTableCountDefault;
|
||||
for (u32 i = MtcTableCountDefault; i < newEmcList.size(); ++i) {
|
||||
if (newEmcList[i] >= emcDvbTableNew[j].freq && newEmcList[i] < emcDvbTableNew[j + 1].freq) {
|
||||
emcDvbTableNew[j].freq = newEmcList[i];
|
||||
++j;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::memset(mem_dvb_table_head, 0, sizeof(EmcDvbTableDefault));
|
||||
std::memcpy(mem_dvb_table_head, &emcDvbTableNew, sizeof(emcDvbTableNew));
|
||||
|
||||
/* Max dvfs entry is 32, but HOS doesn't seem to boot if exact freq doesn't exist in dvb table,
|
||||
reason why it's like this
|
||||
*/
|
||||
@@ -722,8 +854,9 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
}
|
||||
|
||||
Result MemFreqMax(u32 *ptr) {
|
||||
if (C.marikoEmcMaxClock <= EmcClkOSLimit)
|
||||
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
|
||||
R_SUCCEED();
|
||||
@@ -737,12 +870,13 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
I2cSession _session;
|
||||
Result res = i2cOpenSession(&_session, dev);
|
||||
if (R_FAILED(res))
|
||||
if (R_FAILED(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
cmd.reg = reg;
|
||||
cmd.val = val;
|
||||
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
|
||||
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
|
||||
i2csessionClose(&_session);
|
||||
return res;
|
||||
}
|
||||
@@ -751,24 +885,27 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
regulator *entry = reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_2_3.default_uv));
|
||||
|
||||
constexpr u32 uv_step = 5'000;
|
||||
constexpr u32 uv_min = 250'000;
|
||||
constexpr u32 uv_min = 250'000;
|
||||
|
||||
auto validator = [entry]() {
|
||||
R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
|
||||
R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
|
||||
R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
|
||||
R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
|
||||
R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
|
||||
R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
|
||||
R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
|
||||
R_SUCCEED();
|
||||
};
|
||||
|
||||
R_TRY(validator());
|
||||
|
||||
u32 emc_uv = C.marikoEmcVddqVolt;
|
||||
if (!emc_uv)
|
||||
R_SKIP();
|
||||
|
||||
if (emc_uv % uv_step)
|
||||
if (!emc_uv) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
if (emc_uv % uv_step) {
|
||||
emc_uv = (emc_uv + uv_step - 1) / uv_step * uv_step; // rounding
|
||||
}
|
||||
|
||||
PATCH_OFFSET(ptr, emc_uv);
|
||||
|
||||
@@ -783,28 +920,182 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
return resultI2C;
|
||||
}
|
||||
|
||||
Result MemMtcTableAsm(u32 *ptr) {
|
||||
constexpr u32 AddpOffset = 1;
|
||||
constexpr u32 BrOffset = 12;
|
||||
constexpr u32 MovOffset = 10;
|
||||
|
||||
/* Ensure we don't dereference memory before nso start. */
|
||||
R_UNLESS(ptr - BrOffset >= nsoStart, ldr::ResultInvalidMtcTablePattern());
|
||||
|
||||
u32 adrp = *(ptr - AddpOffset);
|
||||
R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTablePattern());
|
||||
|
||||
/* We don't check for matching register because both registers must be x0 in order to pass the previous checks. */
|
||||
/* The correct instructions will always be x0 since the mtcTable pointer is returned. */
|
||||
|
||||
/* Pray this does not break. */
|
||||
u32 br = *(ptr - BrOffset);
|
||||
R_UNLESS(AsmCompareBrNoRd(br, MtcBrAsm), ldr::ResultInvalidMtcTablePattern());
|
||||
|
||||
/* Pray this does not break either. */
|
||||
u32 mov = *(ptr - MovOffset);
|
||||
R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTablePattern());
|
||||
|
||||
u8 movRd = asm_get_rd(mov);
|
||||
u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), movRd);
|
||||
|
||||
PATCH_OFFSET(ptr - BrOffset, NopIns);
|
||||
PATCH_OFFSET(ptr - MovOffset, movCountPatch);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GetSocSpeedo(u32 &socSpeedo) {
|
||||
constexpr u64 FusePhysicalAddress = 0x7000F000;
|
||||
u64 virtualAddress = 0;
|
||||
constexpr u64 Size = 0x1000;
|
||||
|
||||
u64 outSize;
|
||||
/* TODO: use svc::QueryMemoryMapping instead. */
|
||||
R_TRY(svcQueryMemoryMapping(&virtualAddress, &outSize, FusePhysicalAddress, Size));
|
||||
|
||||
constexpr u32 FuseOffset = 2048;
|
||||
constexpr u32 SocSpeedoOffset = 308;
|
||||
socSpeedo = *reinterpret_cast<u32 *>(virtualAddress + FuseOffset + SocSpeedoOffset);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u32 GetSocProcessId(u32 socSpeedo) {
|
||||
if (socSpeedo <= 1597) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (socSpeedo <= 1708) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* >= 1709. */
|
||||
return 2;
|
||||
}
|
||||
|
||||
Result SocVoltAsm(u32 *compareSpeedos) {
|
||||
constexpr u32 VoltageScanLimit = 10;
|
||||
/* Might actually be speedo id. */
|
||||
u32 *writeProcessId = ScanAssembly(compareSpeedos, VoltageScanLimit, SocVoltWriteProcessIdAsm, asm_compare_no_rd);
|
||||
R_UNLESS(writeProcessId != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||
u8 writeProcessIdRd = asm_get_rd(*writeProcessId);
|
||||
|
||||
/* This writes 1050mV. */
|
||||
u32 *writeVoltage = ScanAssembly(writeProcessId, VoltageScanLimit, SocVoltWriteVoltageAsm, asm_compare_no_rd);
|
||||
R_UNLESS(writeVoltage != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||
u8 writeVoltageRd = asm_get_rd(*writeVoltage);
|
||||
|
||||
/* A csel instruction is used to select the soc voltage limit register. */
|
||||
/* We care about its destination register since that is used for verification. */
|
||||
constexpr u32 VoltageSelectScanLimit = 24;
|
||||
u32 *selectVoltage = ScanAssembly(writeVoltage, VoltageSelectScanLimit, SocVoltSelectRegisterAsm, AsmCompareCselNoReg);
|
||||
R_UNLESS(selectVoltage != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||
/* Todo: check rm and rn? */
|
||||
u8 selectVoltageRd = asm_get_rd(*selectVoltage);
|
||||
|
||||
/* rdCsel is then multiplied by 1000 to convert to uV. */
|
||||
/* This is pretty far down the function. */
|
||||
constexpr u32 MultiplierScanLimit = 200;
|
||||
u32 *multiplier = ScanAssembly(selectVoltage, MultiplierScanLimit, SocVoltMultiplyVoltsAsm, AsmCompareMullNoReg);
|
||||
R_UNLESS(multiplier != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||
u8 multiplierRn = AsmGetMullRn(*multiplier);
|
||||
u8 multiplierRm = AsmGetMullRm(*multiplier);
|
||||
/* One of the two registers has to be rdCsel. */
|
||||
R_UNLESS((multiplierRn == selectVoltageRd) || (multiplierRm == selectVoltageRd), ldr::ResultInvalidSocVoltPattern());
|
||||
u8 multiplierRd = asm_get_rd(*multiplier);
|
||||
|
||||
/* Subs instruction is then used to verify against absolute limit. */
|
||||
u32 limitValidationPattern = AsmSubsSetRn(SocVoltValidateLimitAsm, multiplierRd);
|
||||
u32 *limitValidation = ScanAssembly(multiplier, VoltageScanLimit, limitValidationPattern, AsmSubsCompareNoReg);
|
||||
R_UNLESS(limitValidation != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||
|
||||
/* There is a b.gt instruction right after (checks for socVoltageCap < socVoltageMax). */
|
||||
u32 *branchToAbort = limitValidation + 1;
|
||||
R_UNLESS(AsmCompareBrConNoImm19(*branchToAbort, SocVoltBranchToAbortAsm), ldr::ResultInvalidSocVoltPattern());
|
||||
|
||||
if (!C.marikoSocVmax || C.marikoSocVmax <= 1000) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
/* Adjust 1598 speedo minimum to ensure it always goes down process id 0 branch. */
|
||||
/* 2200 should be high enough :D */
|
||||
u32 compareSpeedosPatch = AsmSubsSetImm12(*compareSpeedos, 2200);
|
||||
PATCH_OFFSET(compareSpeedos, compareSpeedosPatch);
|
||||
|
||||
u32 socSpeedo = 0;
|
||||
R_TRY(GetSocSpeedo(socSpeedo));
|
||||
|
||||
/* Adjust processId from 0 to [process id of switch booting this]. */
|
||||
/* We're overwriting the orr instruction entirly. */
|
||||
u32 processId = GetSocProcessId(socSpeedo);
|
||||
u32 writeProcessIdPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, processId), writeProcessIdRd);
|
||||
PATCH_OFFSET(writeProcessId, writeProcessIdPatch);
|
||||
|
||||
/* Adjust voltage limit. */
|
||||
u32 voltageLimitPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, C.marikoSocVmax), writeVoltageRd);
|
||||
PATCH_OFFSET(writeVoltage, voltageLimitPatch);
|
||||
|
||||
/* Branches to an abort if limits are invalid -- we patch the branch instruction with NOP. */
|
||||
PATCH_OFFSET(branchToAbort, NopIns);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SocVoltLimit(u32 *ptr) {
|
||||
R_UNLESS(!std::memcmp(ptr - SocVoltLimitMaxDefaultIndex, socVoltLimitArray, sizeof(socVoltLimitArray)), ldr::ResultInvalidSocVoltLimit());
|
||||
if (!C.marikoSocVmax || C.marikoSocVmax <= SocVoltLimitOfficial) {
|
||||
R_SKIP();
|
||||
}
|
||||
|
||||
constexpr u32 Step = 25;
|
||||
u32 maxVolt = C.marikoSocVmax;
|
||||
if (maxVolt % Step) {
|
||||
maxVolt = maxVolt / Step * Step; /* Round. */
|
||||
}
|
||||
|
||||
u32 volt = SocVoltLimitOfficial;
|
||||
for (u32 i = 1; i < DvfsTableEntryCount - SocVoltLimitMaxDefaultIndex && volt < maxVolt; ++i) {
|
||||
volt += Step;
|
||||
PATCH_OFFSET(ptr + i, volt);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
||||
MtcGenerateFreqTables();
|
||||
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
||||
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
|
||||
|
||||
PatcherEntry<u32> patches[] = {
|
||||
{"CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit},
|
||||
{"CPU Freq Table", CpuFreqCvbTable<true>, 1, nullptr, CpuCvbDefaultMaxFreq},
|
||||
{"CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial},
|
||||
{"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial},
|
||||
{"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0x0000FFCF},
|
||||
{"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial},
|
||||
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial},
|
||||
{"GPU Freq Table", GpuFreqCvbTable<true>, 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 Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit},
|
||||
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit},
|
||||
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit},
|
||||
{"MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault},
|
||||
{"MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default},
|
||||
{ "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit },
|
||||
{ "CPU Freq Table", CpuFreqCvbTable<true>, 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<true>, 1, nullptr, GpuCvbDefaultMaxFreq },
|
||||
{ "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
|
||||
{ "GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
|
||||
{ "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
|
||||
{ "MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
|
||||
{ "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit },
|
||||
{ "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
|
||||
{ "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
|
||||
{ "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault },
|
||||
{ "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default },
|
||||
{ "MEM Table Asm", &MemMtcTableAsm, 1, &MemMtcGetGetTablePatternFn },
|
||||
{ "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn },
|
||||
{ "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial },
|
||||
};
|
||||
|
||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) {
|
||||
@@ -819,6 +1110,8 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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 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;
|
||||
};
|
||||
|
||||
constexpr 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,116 +20,103 @@
|
||||
|
||||
namespace ams::ldr::hoc::ptm {
|
||||
|
||||
Result CpuPtmBoost(perf_conf_entry* entry) {
|
||||
Result CpuPtmBoost(perf_conf_entry* entry) {
|
||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||
|
||||
#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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!confTable) {
|
||||
CRASH("confTable not found!");
|
||||
Result MemPtm(perf_conf_entry* entry) {
|
||||
PATCH_OFFSET(&(entry->emc_freq_1), memPtmLimit);
|
||||
PATCH_OFFSET(&(entry->emc_freq_2), memPtmLimit);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
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 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)) {
|
||||
LOGGING("@%p", &entry);
|
||||
CRASH("Invalid ptm confTable entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (entry->cpu_freq_1) {
|
||||
case cpuPtmBoost:
|
||||
cpuPtmBoostPatch.Apply(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);
|
||||
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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
2
Source/Horizon-OC-Monitor/Horizon-OC-Monitor.lst
Normal file
2
Source/Horizon-OC-Monitor/Horizon-OC-Monitor.lst
Normal file
@@ -0,0 +1,2 @@
|
||||
-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
|
||||
APP_VERSION := 1.3.2+r4-hoc-r4
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
INCLUDES := include lib/Atmosphere-libs/libstratosphere/source/dmnt lib/Atmosphere-libs/libstratosphere/source ../sys-clk/common/include/
|
||||
INCLUDES := include lib/Atmosphere-libs/libstratosphere/source/dmnt lib/Atmosphere-libs/libstratosphere/source ../hoc-clk/common/include/
|
||||
NO_ICON := 1
|
||||
#ROMFS := romfs
|
||||
|
||||
@@ -65,6 +65,7 @@ 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
Source/Horizon-OC-Monitor/README.md
Normal file
1
Source/Horizon-OC-Monitor/README.md
Normal file
@@ -0,0 +1 @@
|
||||
Thanks to NaGa for Status Monitor Pro!
|
||||
Submodule Source/Horizon-OC-Monitor/lib/Atmosphere-libs updated: 6cc765fcaa...82f1553c4c
Submodule Source/Horizon-OC-Monitor/lib/libultrahand updated: 8541ecbe95...3e54f2a1ad
File diff suppressed because it is too large
Load Diff
@@ -129,10 +129,8 @@ 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);
|
||||
|
||||
@@ -158,7 +156,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);
|
||||
@@ -369,11 +367,8 @@ 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);
|
||||
@@ -424,8 +419,6 @@ public:
|
||||
tsl::hlp::doWithSmSession([this]{
|
||||
|
||||
apmInitialize();
|
||||
if (hosversionAtLeast(8,0,0)) clkrstCheck = clkrstInitialize();
|
||||
else pcvCheck = pcvInitialize();
|
||||
|
||||
if (hosversionAtLeast(5,0,0)) tcCheck = tcInitialize();
|
||||
|
||||
@@ -446,6 +439,14 @@ 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);
|
||||
@@ -458,18 +459,18 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
sysclkIpcExit();
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
shmemClose(&_sharedmemory);
|
||||
//Exit services
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -497,8 +498,6 @@ 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");
|
||||
|
||||
@@ -520,6 +519,14 @@ 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);
|
||||
@@ -532,7 +539,6 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -540,10 +546,10 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
sysclkIpcExit();
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
//Exit services
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -575,8 +581,6 @@ 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");
|
||||
|
||||
@@ -598,6 +602,14 @@ 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);
|
||||
@@ -610,7 +622,6 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
|
||||
@@ -619,10 +630,10 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
sysclkIpcExit();
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
// Exit services
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -656,8 +667,6 @@ 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");
|
||||
|
||||
@@ -679,6 +688,14 @@ 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);
|
||||
@@ -691,7 +708,6 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -699,9 +715,9 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -730,8 +746,6 @@ 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");
|
||||
|
||||
@@ -753,6 +767,14 @@ 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);
|
||||
@@ -765,7 +787,6 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -773,9 +794,9 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -804,8 +825,6 @@ 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");
|
||||
|
||||
@@ -827,6 +846,14 @@ 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);
|
||||
@@ -839,7 +866,6 @@ public:
|
||||
}
|
||||
}
|
||||
splExit();
|
||||
sysclkIpcInitialize();
|
||||
});
|
||||
Hinted = envIsSyscallHinted(0x6F);
|
||||
}
|
||||
@@ -847,9 +873,9 @@ public:
|
||||
virtual void exitServices() override {
|
||||
CloseThreads();
|
||||
shmemClose(&_sharedmemory);
|
||||
sysclkIpcExit();
|
||||
clkrstExit();
|
||||
pcvExit();
|
||||
if (R_SUCCEEDED(hocclkCheck)) {
|
||||
hocclkIpcExit();
|
||||
}
|
||||
tsExit();
|
||||
tcExit();
|
||||
pwmChannelSessionClose(&g_ICon);
|
||||
@@ -931,7 +957,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 Horizon OC Monitor directive)
|
||||
// load heap settings outside of loop (only Status 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,6 +13,9 @@ 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;
|
||||
|
||||
@@ -402,19 +405,37 @@ 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: 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);
|
||||
}
|
||||
});
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tsl::elm::HeaderOverlayFrame* rootFrame = new tsl::elm::HeaderOverlayFrame("", "");
|
||||
rootFrame->setContent(Status);
|
||||
@@ -470,6 +491,16 @@ 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);
|
||||
@@ -496,8 +527,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[SysClkPartLoad_EMC] / 10,
|
||||
partLoad[SysClkPartLoad_EMC] % 10);
|
||||
partLoad[HocClkPartLoad_EMC] / 10,
|
||||
partLoad[HocClkPartLoad_EMC] % 10);
|
||||
|
||||
mutexUnlock(&mutex_Misc);
|
||||
|
||||
|
||||
@@ -20,9 +20,17 @@ 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] = "";
|
||||
|
||||
@@ -117,7 +125,7 @@ public:
|
||||
|
||||
//Print strings
|
||||
///CPU
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
||||
if (1) {
|
||||
|
||||
uint32_t height_offset = 155;
|
||||
if (realCPU_Hz && settings.showRealFreqs) {
|
||||
@@ -165,7 +173,7 @@ public:
|
||||
}
|
||||
|
||||
///GPU
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck) || R_SUCCEEDED(nvCheck)) {
|
||||
if (R_SUCCEEDED(nvCheck)) {
|
||||
|
||||
uint32_t height_offset = 320-8;
|
||||
if (realGPU_Hz && settings.showRealFreqs) {
|
||||
@@ -173,7 +181,7 @@ public:
|
||||
}
|
||||
|
||||
renderer->drawString("GPU Usage", false, COMMON_MARGIN, 285-8, 20, (settings.catColor1));
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
||||
if (1) {
|
||||
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));
|
||||
@@ -205,7 +213,7 @@ public:
|
||||
static std::vector<std::string> specialChars = {""};
|
||||
|
||||
///RAM
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck) || R_SUCCEEDED(Hinted)) {
|
||||
if (R_SUCCEEDED(Hinted)) {
|
||||
|
||||
uint32_t height_offset = 410;
|
||||
if (realRAM_Hz && settings.showRealFreqs) {
|
||||
@@ -213,7 +221,7 @@ public:
|
||||
}
|
||||
|
||||
renderer->drawString("RAM Usage", false, COMMON_MARGIN, 375, 20, (settings.catColor1));
|
||||
if (R_SUCCEEDED(clkrstCheck) || R_SUCCEEDED(pcvCheck)) {
|
||||
if (1) {
|
||||
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));
|
||||
@@ -233,16 +241,41 @@ public:
|
||||
else if (realRAM_Hz && settings.showDeltas && (settings.showRealFreqs || settings.showTargetFreqs)) {
|
||||
renderer->drawString(DeltaRAM_c, false, COMMON_MARGIN + deltaOffset, height_offset, 15, (settings.textColor));
|
||||
}
|
||||
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(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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, 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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +322,41 @@ 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) {
|
||||
@@ -443,13 +511,28 @@ public:
|
||||
RAMPct_systemunsafe
|
||||
);
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
///Thermal
|
||||
snprintf(SOC_temperature_c, sizeof SOC_temperature_c, "%.1f\u00B0C", SOC_temperatureF);
|
||||
@@ -458,6 +541,17 @@ 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,125 +29,128 @@
|
||||
#include <switch.h>
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sysclk/client/ipc.h>
|
||||
#include <hocclk/client/ipc.h>
|
||||
|
||||
static Service g_sysclkSrv;
|
||||
static Service g_hocclkSrv;
|
||||
static atomic_size_t g_refCnt;
|
||||
|
||||
bool sysclkIpcRunning()
|
||||
bool hocclkIpcRunning()
|
||||
{
|
||||
Handle handle;
|
||||
bool running = R_FAILED(smRegisterService(&handle, smEncodeName(SYSCLK_IPC_SERVICE_NAME), false, 1));
|
||||
bool running = R_FAILED(smRegisterService(&handle, smEncodeName(HOCCLK_IPC_SERVICE_NAME), false, 1));
|
||||
|
||||
if (!running)
|
||||
{
|
||||
smUnregisterService(smEncodeName(SYSCLK_IPC_SERVICE_NAME));
|
||||
smUnregisterService(smEncodeName(HOCCLK_IPC_SERVICE_NAME));
|
||||
}
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
Result sysclkIpcInitialize(void)
|
||||
Result hocclkIpcInitialize(void)
|
||||
{
|
||||
Result rc = 0;
|
||||
|
||||
g_refCnt++;
|
||||
|
||||
if (serviceIsActive(&g_sysclkSrv))
|
||||
if (serviceIsActive(&g_hocclkSrv))
|
||||
return 0;
|
||||
|
||||
rc = smGetService(&g_sysclkSrv, SYSCLK_IPC_SERVICE_NAME);
|
||||
rc = smGetService(&g_hocclkSrv, HOCCLK_IPC_SERVICE_NAME);
|
||||
|
||||
if (R_FAILED(rc)) sysclkIpcExit();
|
||||
if (R_FAILED(rc)) hocclkIpcExit();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void sysclkIpcExit(void)
|
||||
void hocclkIpcExit(void)
|
||||
{
|
||||
if (--g_refCnt == 0)
|
||||
{
|
||||
serviceClose(&g_sysclkSrv);
|
||||
serviceClose(&g_hocclkSrv);
|
||||
}
|
||||
}
|
||||
|
||||
Result sysclkIpcGetAPIVersion(u32* out_ver)
|
||||
Result hocclkIpcGetAPIVersion(u32* out_ver)
|
||||
{
|
||||
return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetApiVersion, *out_ver);
|
||||
return serviceDispatchOut(&g_hocclkSrv, HocClkIpcCmd_GetApiVersion, *out_ver);
|
||||
}
|
||||
|
||||
Result sysclkIpcGetVersionString(char* out, size_t len)
|
||||
Result hocclkIpcGetVersionString(char* out, size_t len)
|
||||
{
|
||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetVersionString,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out, len}},
|
||||
);
|
||||
}
|
||||
|
||||
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
|
||||
Result hocclkIpcGetCurrentContext(HocClkContext* out_context)
|
||||
{
|
||||
return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext, *out_context);
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetCurrentContext,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_context, sizeof(HocClkContext)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
|
||||
Result hocclkIpcGetProfileCount(u64 tid, u8* out_count)
|
||||
{
|
||||
return serviceDispatchInOut(&g_sysclkSrv, SysClkIpcCmd_GetProfileCount, tid, *out_count);
|
||||
return serviceDispatchInOut(&g_hocclkSrv, HocClkIpcCmd_GetProfileCount, tid, *out_count);
|
||||
}
|
||||
|
||||
Result sysclkIpcSetEnabled(bool enabled)
|
||||
Result hocclkIpcSetEnabled(bool enabled)
|
||||
{
|
||||
u8 enabledRaw = (u8)enabled;
|
||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetEnabled, enabledRaw);
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetEnabled, enabledRaw);
|
||||
}
|
||||
|
||||
Result sysclkIpcSetOverride(SysClkModule module, u32 hz)
|
||||
Result hocclkIpcSetOverride(HocClkModule module, u32 hz)
|
||||
{
|
||||
SysClkIpc_SetOverride_Args args = {
|
||||
HocClkIpc_SetOverride_Args args = {
|
||||
.module = module,
|
||||
.hz = hz
|
||||
};
|
||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetOverride, args);
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetOverride, args);
|
||||
}
|
||||
|
||||
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
|
||||
Result hocclkIpcGetProfiles(u64 tid, HocClkTitleProfileList* out_profiles)
|
||||
{
|
||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}},
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetProfiles, tid,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_profiles, sizeof(HocClkTitleProfileList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles)
|
||||
Result hocclkIpcSetProfiles(u64 tid, HocClkTitleProfileList* profiles)
|
||||
{
|
||||
SysClkIpc_SetProfiles_Args args;
|
||||
HocClkIpc_SetProfiles_Args args;
|
||||
args.tid = tid;
|
||||
memcpy(&args.profiles, profiles, sizeof(SysClkTitleProfileList));
|
||||
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetProfiles, args);
|
||||
memcpy(&args.profiles, profiles, sizeof(HocClkTitleProfileList));
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetProfiles, args);
|
||||
}
|
||||
|
||||
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
|
||||
Result hocclkIpcGetConfigValues(HocClkConfigValueList* out_configValues)
|
||||
{
|
||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
|
||||
.buffers = {{out_configValues, sizeof(SysClkConfigValueList)}},
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_GetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{out_configValues, sizeof(HocClkConfigValueList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
|
||||
Result hocclkIpcSetConfigValues(HocClkConfigValueList* configValues)
|
||||
{
|
||||
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
|
||||
.buffers = {{configValues, sizeof(SysClkConfigValueList)}},
|
||||
return serviceDispatch(&g_hocclkSrv, HocClkIpcCmd_SetConfigValues,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
|
||||
.buffers = {{configValues, sizeof(HocClkConfigValueList)}},
|
||||
);
|
||||
}
|
||||
|
||||
Result sysclkIpcGetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* outCount)
|
||||
Result hocclkIpcGetFreqList(HocClkModule module, u32* list, u32 maxCount, u32* outCount)
|
||||
{
|
||||
SysClkIpc_GetFreqList_Args args = {
|
||||
HocClkIpc_GetFreqList_Args args = {
|
||||
.module = module,
|
||||
.maxCount = maxCount
|
||||
};
|
||||
return serviceDispatchInOut(&g_sysclkSrv, SysClkIpcCmd_GetFreqList, args, *outCount,
|
||||
return serviceDispatchInOut(&g_hocclkSrv, HocClkIpcCmd_GetFreqList, args, *outCount,
|
||||
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
|
||||
.buffers = {{list, maxCount * sizeof(u32)}},
|
||||
);
|
||||
@@ -156,11 +159,11 @@ Result sysclkIpcGetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* o
|
||||
Result hocClkIpcSetKipData()
|
||||
{
|
||||
u32 temp = 0;
|
||||
return serviceDispatchIn(&g_sysclkSrv, HocClkIpcCmd_SetKipData, temp);
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetKipData, temp);
|
||||
}
|
||||
|
||||
Result hocClkIpcGetKipData()
|
||||
{
|
||||
u32 temp = 0;
|
||||
return serviceDispatchIn(&g_sysclkSrv, HocClkIpcCmd_GetKipData, temp);
|
||||
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetKipData, temp);
|
||||
}
|
||||
@@ -0,0 +1,897 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
938
Source/Old-Atmosphere-Patches/ldr_process_creation_1_11_0.cpp
Normal file
938
Source/Old-Atmosphere-Patches/ldr_process_creation_1_11_0.cpp
Normal file
@@ -0,0 +1,938 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
780
Source/Old-Atmosphere-Patches/ldr_process_creation_1_9_5.cpp
Normal file
780
Source/Old-Atmosphere-Patches/ldr_process_creation_1_9_5.cpp
Normal file
@@ -0,0 +1,780 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
81
Source/fatal_handler_payload/Makefile
Normal file
81
Source/fatal_handler_payload/Makefile
Normal file
@@ -0,0 +1,81 @@
|
||||
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)"
|
||||
0
Source/fatal_handler_payload/compile_flags.txt
Normal file
0
Source/fatal_handler_payload/compile_flags.txt
Normal file
558
Source/fatal_handler_payload/fatal_handler/gfx/gfx.c
Normal file
558
Source/fatal_handler_payload/fatal_handler/gfx/gfx.c
Normal file
@@ -0,0 +1,558 @@
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
}
|
||||
94
Source/fatal_handler_payload/fatal_handler/gfx/gfx.h
Normal file
94
Source/fatal_handler_payload/fatal_handler/gfx/gfx.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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
|
||||
114
Source/fatal_handler_payload/fatal_handler/hos/secmon_exo.c
Normal file
114
Source/fatal_handler_payload/fatal_handler/hos/secmon_exo.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
24
Source/fatal_handler_payload/fatal_handler/hos/secmon_exo.h
Normal file
24
Source/fatal_handler_payload/fatal_handler/hos/secmon_exo.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2021 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _SECMON_EXO_H_
|
||||
#define _SECMON_EXO_H_
|
||||
|
||||
#include <bdk.h>
|
||||
|
||||
void secmon_exo_check_panic();
|
||||
|
||||
#endif
|
||||
25
Source/fatal_handler_payload/fatal_handler/link.ld
Normal file
25
Source/fatal_handler_payload/fatal_handler/link.ld
Normal file
@@ -0,0 +1,25 @@
|
||||
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 = .;
|
||||
}
|
||||
}
|
||||
92
Source/fatal_handler_payload/fatal_handler/main.c
Normal file
92
Source/fatal_handler_payload/fatal_handler/main.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
78
Source/fatal_handler_payload/fatal_handler/start.S
Normal file
78
Source/fatal_handler_payload/fatal_handler/start.S
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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
|
||||
22
Source/hoc-clk/MIGRATION.md
Normal file
22
Source/hoc-clk/MIGRATION.md
Normal file
@@ -0,0 +1,22 @@
|
||||
** 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
|
||||
5
Source/hoc-clk/README.md
Normal file
5
Source/hoc-clk/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# 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
|
||||
BIN
Source/hoc-clk/assets/hoc.rgba
Normal file
BIN
Source/hoc-clk/assets/hoc.rgba
Normal file
Binary file not shown.
@@ -9,10 +9,8 @@ if [[ -n "$1" ]]; then
|
||||
DIST_DIR="$1"
|
||||
fi
|
||||
|
||||
echo "DIST_DIR: $DIST_DIR"
|
||||
echo "CORES: $CORES"
|
||||
|
||||
echo "*** sysmodule ***"
|
||||
echo
|
||||
echo "*** Compiling hoc-clk ***"
|
||||
TITLE_ID="$(grep -oP '"title_id":\s*"0x\K(\w+)' "$ROOT_DIR/sysmodule/perms.json")"
|
||||
|
||||
pushd "$ROOT_DIR/sysmodule"
|
||||
@@ -20,23 +18,27 @@ make -j$CORES
|
||||
popd > /dev/null
|
||||
|
||||
mkdir -p "$DIST_DIR/atmosphere/contents/$TITLE_ID/flags"
|
||||
cp -vf "$ROOT_DIR/sysmodule/out/horizon-oc.nsp" "$DIST_DIR/atmosphere/contents/$TITLE_ID/exefs.nsp"
|
||||
cp -vf "$ROOT_DIR/sysmodule/out/hoc-clk.nsp" "$DIST_DIR/atmosphere/contents/$TITLE_ID/exefs.nsp"
|
||||
>"$DIST_DIR/atmosphere/contents/$TITLE_ID/flags/boot2.flag"
|
||||
cp -vf "$ROOT_DIR/sysmodule/toolbox.json" "$DIST_DIR/atmosphere/contents/$TITLE_ID/toolbox.json"
|
||||
|
||||
echo "*** overlay ***"
|
||||
echo
|
||||
echo "*** Compiling hoc-clk-overlay ***"
|
||||
pushd "$ROOT_DIR/overlay"
|
||||
make -j$CORES
|
||||
popd > /dev/null
|
||||
|
||||
mkdir -p "$DIST_DIR/switch/.overlays"
|
||||
cp -vf "$ROOT_DIR/overlay/out/horizon-oc-overlay.ovl" "$DIST_DIR/switch/.overlays/horizon-oc-overlay.ovl"
|
||||
echo
|
||||
|
||||
echo "*** assets ***"
|
||||
echo "*** Copying assets ***"
|
||||
mkdir -p "$DIST_DIR/config/horizon-oc"
|
||||
cp -vf "$ROOT_DIR/config.ini.template" "$DIST_DIR/config/horizon-oc/config.ini.template"
|
||||
cp -vf "$ROOT_DIR/../../README.md" "$DIST_DIR/README.md"
|
||||
mkdir -p "$DIST_DIR/config/ultrahand/assets/notifications"
|
||||
cp -vf "$ROOT_DIR/assets/hoc.rgba" "$DIST_DIR/config/ultrahand/assets/notifications/hoc.rgba"
|
||||
|
||||
echo "*** lang ***"
|
||||
|
||||
cp -r "$ROOT_DIR/overlay/lang/" "$DIST_DIR/config/horizon-oc/lang/"
|
||||
echo
|
||||
echo "*** Copying lang ***"
|
||||
cp -vr "$ROOT_DIR/overlay/lang/" "$DIST_DIR/config/horizon-oc/lang/"
|
||||
echo
|
||||
248
Source/hoc-clk/common/include/SaltyNX.h
Normal file
248
Source/hoc-clk/common/include/SaltyNX.h
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -12,9 +12,9 @@
|
||||
*
|
||||
* 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>
|
||||
@@ -42,13 +42,13 @@ extern "C" {
|
||||
// typedef std::int16_t s16;
|
||||
// typedef std::uint16_t u16;
|
||||
|
||||
#include "sysclk/ipc.h"
|
||||
#include "sysclk/board.h"
|
||||
#include "sysclk/clock_manager.h"
|
||||
#include "sysclk/apm.h"
|
||||
#include "sysclk/config.h"
|
||||
#include "sysclk/errors.h"
|
||||
#include "sysclk/psm_ext.h"
|
||||
#include "hocclk/ipc.h"
|
||||
#include "hocclk/board.h"
|
||||
#include "hocclk/clock_manager.h"
|
||||
#include "hocclk/apm.h"
|
||||
#include "hocclk/config.h"
|
||||
#include "hocclk/errors.h"
|
||||
#include "hocclk/result.hpp"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
@@ -34,6 +34,6 @@ typedef struct {
|
||||
uint32_t cpu_hz;
|
||||
uint32_t gpu_hz;
|
||||
uint32_t mem_hz;
|
||||
} SysClkApmConfiguration;
|
||||
} HocClkApmConfiguration;
|
||||
|
||||
extern SysClkApmConfiguration sysclk_g_apm_configurations[];
|
||||
extern HocClkApmConfiguration hocclk_g_apm_configurations[];
|
||||
404
Source/hoc-clk/common/include/hocclk/board.h
Normal file
404
Source/hoc-clk/common/include/hocclk/board.h
Normal file
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
* 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";
|
||||
// }
|
||||
// }
|
||||
@@ -32,26 +32,26 @@
|
||||
#include "../board.h"
|
||||
#include "../ipc.h"
|
||||
|
||||
bool sysclkIpcRunning();
|
||||
Result sysclkIpcInitialize(void);
|
||||
void sysclkIpcExit(void);
|
||||
bool hocclkIpcRunning();
|
||||
Result hocclkIpcInitialize(void);
|
||||
void hocclkIpcExit(void);
|
||||
|
||||
Result sysclkIpcGetAPIVersion(u32* out_ver);
|
||||
Result sysclkIpcGetVersionString(char* out, size_t len);
|
||||
Result sysclkIpcGetCurrentContext(SysClkContext* out_context);
|
||||
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count);
|
||||
Result sysclkIpcSetEnabled(bool enabled);
|
||||
Result sysclkIpcExitCmd();
|
||||
Result sysclkIpcSetOverride(SysClkModule module, u32 hz);
|
||||
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles);
|
||||
Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles);
|
||||
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues);
|
||||
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues);
|
||||
Result sysclkIpcGetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* outCount);
|
||||
Result hocclkIpcGetAPIVersion(u32* out_ver);
|
||||
Result hocclkIpcGetVersionString(char* out, size_t len);
|
||||
Result hocclkIpcGetCurrentContext(HocClkContext* out_context);
|
||||
Result hocclkIpcGetProfileCount(u64 tid, u8* out_count);
|
||||
Result hocclkIpcSetEnabled(bool enabled);
|
||||
Result hocclkIpcExitCmd();
|
||||
Result hocclkIpcSetOverride(HocClkModule module, u32 hz);
|
||||
Result hocclkIpcGetProfiles(u64 tid, HocClkTitleProfileList* out_profiles);
|
||||
Result hocclkIpcSetProfiles(u64 tid, HocClkTitleProfileList* profiles);
|
||||
Result hocclkIpcGetConfigValues(HocClkConfigValueList* out_configValues);
|
||||
Result hocclkIpcSetConfigValues(HocClkConfigValueList* configValues);
|
||||
Result hocclkIpcGetFreqList(HocClkModule module, u32* list, u32 maxCount, u32* outCount);
|
||||
Result hocClkIpcSetKipData();
|
||||
Result hocClkIpcGetKipData();
|
||||
|
||||
static inline Result sysclkIpcRemoveOverride(SysClkModule module)
|
||||
static inline Result hocclkIpcRemoveOverride(HocClkModule module)
|
||||
{
|
||||
return sysclkIpcSetOverride(module, 0);
|
||||
return hocclkIpcSetOverride(module, 0);
|
||||
}
|
||||
101
Source/hoc-clk/common/include/hocclk/clock_manager.h
Normal file
101
Source/hoc-clk/common/include/hocclk/clock_manager.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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);
|
||||
@@ -29,13 +29,13 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board.h"
|
||||
typedef enum {
|
||||
SysClkConfigValue_PollingIntervalMs = 0,
|
||||
SysClkConfigValue_TempLogIntervalMs,
|
||||
SysClkConfigValue_FreqLogIntervalMs,
|
||||
SysClkConfigValue_PowerLogIntervalMs,
|
||||
SysClkConfigValue_CsvWriteIntervalMs,
|
||||
HocClkConfigValue_PollingIntervalMs = 0,
|
||||
HocClkConfigValue_TempLogIntervalMs,
|
||||
HocClkConfigValue_FreqLogIntervalMs,
|
||||
HocClkConfigValue_PowerLogIntervalMs,
|
||||
HocClkConfigValue_CsvWriteIntervalMs,
|
||||
|
||||
HocClkConfigValue_UncappedClocks,
|
||||
HocClkConfigValue_OverwriteBoostMode,
|
||||
@@ -51,18 +51,30 @@ typedef enum {
|
||||
|
||||
HocClkConfigValue_LiteTDPLimit,
|
||||
|
||||
HorizonOCConfigValue_BatteryChargeCurrent,
|
||||
HocClkConfigValue_BatteryChargeCurrent,
|
||||
|
||||
HorizonOCConfigValue_OverwriteRefreshRate,
|
||||
HorizonOCConfigValue_EnableUnsafeDisplayFreqs,
|
||||
HocClkConfigValue_OverwriteRefreshRate,
|
||||
HocClkConfigValue_MaxDisplayClockH,
|
||||
|
||||
HorizonOCConfigValue_DVFSMode,
|
||||
HorizonOCConfigValue_DVFSOffset,
|
||||
HorizonOCConfigValue_LiveCpuUv,
|
||||
HorizonOCConfigValue_EnableExperimentalSettings,
|
||||
HocClkConfigValue_DVFSMode,
|
||||
HocClkConfigValue_DVFSOffset,
|
||||
HocClkConfigValue_LiveCpuUv,
|
||||
HocClkConfigValue_EnableExperimentalSettings,
|
||||
|
||||
HocClkConfigValue_GPUScheduling,
|
||||
HocClkConfigValue_GPUSchedulingMethod,
|
||||
|
||||
HocClkConfigValue_RAMVoltDisplayMode,
|
||||
HocClkConfigValue_CpuGovernorMinimumFreq,
|
||||
HocClkConfigValue_DisplayVoltage,
|
||||
|
||||
HocClkConfigValue_MemoryFrequencyMeasurementMode,
|
||||
HocClkConfigValue_RamDisplayUnit,
|
||||
HocClkConfigValue_IsFirstLoad,
|
||||
|
||||
HocClkConfigValue_AulaDisplayColorPreset,
|
||||
HocClkConfigValue_MarikoMiddleFreqs,
|
||||
|
||||
HorizonOCConfigValue_GPUScheduling,
|
||||
HorizonOCConfigValue_GPUSchedulingMethod,
|
||||
KipConfigValue_custRev,
|
||||
// KipConfigValue_mtcConf,
|
||||
KipConfigValue_hpMode,
|
||||
@@ -71,9 +83,12 @@ typedef enum {
|
||||
KipConfigValue_eristaEmcMaxClock,
|
||||
KipConfigValue_eristaEmcMaxClock1,
|
||||
KipConfigValue_eristaEmcMaxClock2,
|
||||
|
||||
KipConfigValue_stepMode,
|
||||
KipConfigValue_marikoEmcMaxClock,
|
||||
KipConfigValue_marikoEmcVddqVolt,
|
||||
KipConfigValue_emcDvbShift,
|
||||
KipConfigValue_marikoSocVmax,
|
||||
|
||||
KipConfigValue_t1_tRCD,
|
||||
KipConfigValue_t2_tRP,
|
||||
@@ -83,6 +98,23 @@ typedef enum {
|
||||
KipConfigValue_t6_tRTW,
|
||||
KipConfigValue_t7_tWTR,
|
||||
KipConfigValue_t8_tREFI,
|
||||
|
||||
KipConfigValue_timingEmcTbreak,
|
||||
KipConfigValue_low_t6_tRTW,
|
||||
KipConfigValue_low_t7_tWTR,
|
||||
|
||||
KipConfigValue_t2_tRP_cap,
|
||||
|
||||
KipConfigValue_read_latency_1333,
|
||||
KipConfigValue_read_latency_1600,
|
||||
KipConfigValue_read_latency_1866,
|
||||
KipConfigValue_read_latency_2133,
|
||||
|
||||
KipConfigValue_write_latency_1333,
|
||||
KipConfigValue_write_latency_1600,
|
||||
KipConfigValue_write_latency_1866,
|
||||
KipConfigValue_write_latency_2133,
|
||||
|
||||
KipConfigValue_mem_burst_read_latency,
|
||||
KipConfigValue_mem_burst_write_latency,
|
||||
|
||||
@@ -168,27 +200,26 @@ typedef enum {
|
||||
KipConfigValue_t7_tWTR_fine_tune,
|
||||
|
||||
KipCrc32,
|
||||
HocClkConfigValue_IsFirstLoad,
|
||||
SysClkConfigValue_EnumMax,
|
||||
} SysClkConfigValue;
|
||||
HocClkConfigValue_EnumMax,
|
||||
} HocClkConfigValue;
|
||||
|
||||
typedef struct {
|
||||
uint64_t values[SysClkConfigValue_EnumMax];
|
||||
} SysClkConfigValueList;
|
||||
uint64_t values[HocClkConfigValue_EnumMax];
|
||||
} HocClkConfigValueList;
|
||||
|
||||
static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pretty)
|
||||
static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pretty)
|
||||
{
|
||||
switch(val)
|
||||
{
|
||||
case SysClkConfigValue_PollingIntervalMs:
|
||||
case HocClkConfigValue_PollingIntervalMs:
|
||||
return pretty ? "Polling Interval (ms)" : "poll_interval_ms";
|
||||
case SysClkConfigValue_TempLogIntervalMs:
|
||||
case HocClkConfigValue_TempLogIntervalMs:
|
||||
return pretty ? "Temperature logging interval (ms)" : "temp_log_interval_ms";
|
||||
case SysClkConfigValue_FreqLogIntervalMs:
|
||||
case HocClkConfigValue_FreqLogIntervalMs:
|
||||
return pretty ? "Frequency logging interval (ms)" : "freq_log_interval_ms";
|
||||
case SysClkConfigValue_PowerLogIntervalMs:
|
||||
case HocClkConfigValue_PowerLogIntervalMs:
|
||||
return pretty ? "Power logging interval (ms)" : "power_log_interval_ms";
|
||||
case SysClkConfigValue_CsvWriteIntervalMs:
|
||||
case HocClkConfigValue_CsvWriteIntervalMs:
|
||||
return pretty ? "CSV write interval (ms)" : "csv_write_interval_ms";
|
||||
|
||||
case HocClkConfigValue_UncappedClocks:
|
||||
@@ -197,7 +228,7 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
|
||||
return pretty ? "Overwrite Boost Mode" : "ow_boost";
|
||||
|
||||
case HocClkConfigValue_EristaMaxCpuClock:
|
||||
return pretty ? "CPU Max Display Clock" : "cpu_max_e";
|
||||
return pretty ? "CPU Max Clock" : "cpu_max_e";
|
||||
|
||||
case HocClkConfigValue_MarikoMaxCpuClock:
|
||||
return pretty ? "CPU Max Display Clock" : "cpu_max_m";
|
||||
@@ -217,33 +248,50 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
|
||||
case HocClkConfigValue_LiteTDPLimit:
|
||||
return pretty ? "Handheld TDP Limit" : "tdp_limit_l";
|
||||
|
||||
case HorizonOCConfigValue_BatteryChargeCurrent:
|
||||
case HocClkConfigValue_BatteryChargeCurrent:
|
||||
return pretty ? "Battery Charge Current" : "bat_charge_current";
|
||||
|
||||
case HorizonOCConfigValue_OverwriteRefreshRate:
|
||||
case HocClkConfigValue_OverwriteRefreshRate:
|
||||
return pretty ? "Display Refresh Rate Changing" : "drr_changing";
|
||||
|
||||
case HorizonOCConfigValue_EnableUnsafeDisplayFreqs:
|
||||
return pretty ? "Enable Unsafe Display Frequencies" : "drr_unsafe";
|
||||
case HocClkConfigValue_MaxDisplayClockH:
|
||||
return pretty ? "Max Display Clock (Handheld)" : "drr_max_clock";
|
||||
|
||||
case HorizonOCConfigValue_DVFSMode:
|
||||
case HocClkConfigValue_DVFSMode:
|
||||
return pretty ? "DVFS Mode" : "dvfs_mode";
|
||||
|
||||
case HorizonOCConfigValue_DVFSOffset:
|
||||
case HocClkConfigValue_DVFSOffset:
|
||||
return pretty ? "DVFS Offset" : "dvfs_offset";
|
||||
|
||||
case HorizonOCConfigValue_GPUScheduling:
|
||||
case HocClkConfigValue_GPUScheduling:
|
||||
return pretty ? "GPU Scheduling" : "gpu_scheduling";
|
||||
|
||||
case HorizonOCConfigValue_GPUSchedulingMethod:
|
||||
case HocClkConfigValue_GPUSchedulingMethod:
|
||||
return pretty ? "GPU Scheduling Method" : "gpu_sched_method";
|
||||
|
||||
case HorizonOCConfigValue_LiveCpuUv:
|
||||
case HocClkConfigValue_LiveCpuUv:
|
||||
return pretty ? "Live CPU Undervolt" : "live_cpu_uv";
|
||||
|
||||
case HorizonOCConfigValue_EnableExperimentalSettings:
|
||||
case HocClkConfigValue_EnableExperimentalSettings:
|
||||
return pretty ? "Enable Experimental Settings" : "enable_experimental_settings";
|
||||
|
||||
case HocClkConfigValue_RAMVoltDisplayMode:
|
||||
return pretty ? "RAM Voltage / Usage Display Mode" : "ram_volt_usage_display_mode";
|
||||
case HocClkConfigValue_CpuGovernorMinimumFreq:
|
||||
return pretty ? "CPU Governor Minimum Frequency" : "cpu_gov_min_freq";
|
||||
|
||||
case HocClkConfigValue_DisplayVoltage:
|
||||
return pretty ? "Display Voltage" : "display_voltage";
|
||||
|
||||
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
|
||||
return pretty ? "RAM Frequency Measurement Mode" : "mem_freq_measurement_mode";
|
||||
|
||||
case HocClkConfigValue_RamDisplayUnit:
|
||||
return pretty ? "RAM Frequency Display Unit" : "RAM_display_unit";
|
||||
case HocClkConfigValue_AulaDisplayColorPreset:
|
||||
return pretty ? "Aula Display Color Preset" : "aula_color_preset";
|
||||
case HocClkConfigValue_MarikoMiddleFreqs:
|
||||
return pretty ? "Mariko Middle Clocks" : "mariko_middle_freqs";
|
||||
// KIP config values
|
||||
case KipConfigValue_custRev:
|
||||
return pretty ? "Custom Revision" : "kip_cust_rev";
|
||||
@@ -261,13 +309,16 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
|
||||
return pretty ? "Erista EMC Max Clock 2" : "erista_emc_max_clock1";
|
||||
case KipConfigValue_eristaEmcMaxClock2:
|
||||
return pretty ? "Erista EMC Max Clock 3" : "erista_emc_max_clock2";
|
||||
case KipConfigValue_stepMode:
|
||||
return pretty ? "Step Mode:" : "step_mode";
|
||||
case KipConfigValue_marikoEmcMaxClock:
|
||||
return pretty ? "Mariko EMC Max Clock" : "mariko_emc_max_clock";
|
||||
case KipConfigValue_marikoEmcVddqVolt:
|
||||
return pretty ? "Mariko EMC VDDQ Voltage" : "mariko_emc_vddq_volt";
|
||||
case KipConfigValue_emcDvbShift:
|
||||
return pretty ? "EMC DVB Shift" : "emc_dvb_shift";
|
||||
|
||||
case KipConfigValue_marikoSocVmax:
|
||||
return pretty ? "SOC Vmax" : "soc_vmax";
|
||||
// Memory timings
|
||||
case KipConfigValue_t1_tRCD:
|
||||
return pretty ? "t1 - tRCD" : "t1_trcd";
|
||||
@@ -285,6 +336,35 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
|
||||
return pretty ? "t7 - tWTR" : "t7_twtr";
|
||||
case KipConfigValue_t8_tREFI:
|
||||
return pretty ? "t8 - tREFI" : "t8_trefi";
|
||||
|
||||
case KipConfigValue_timingEmcTbreak:
|
||||
return pretty ? "Timing Emc Tbreak" : "timingEmcTbreak";
|
||||
case KipConfigValue_low_t6_tRTW:
|
||||
return pretty ? "Low T6 - tRTW" : "low_t6_tRTW";
|
||||
case KipConfigValue_low_t7_tWTR:
|
||||
return pretty ? "Low T7 - tWTR" : "low_t7_tWTR";
|
||||
|
||||
case KipConfigValue_t2_tRP_cap:
|
||||
return pretty ? "t2 - trp 1333WL Cap" : "t2_tRP_cap";
|
||||
|
||||
case KipConfigValue_read_latency_1333:
|
||||
return pretty ? "1333 Read Latency" : "read_latency_1333";
|
||||
case KipConfigValue_read_latency_1600:
|
||||
return pretty ? "1600 Read Latency" : "read_latency_1600";
|
||||
case KipConfigValue_read_latency_1866:
|
||||
return pretty ? "1866 Read Latency" : "read_latency_1866";
|
||||
case KipConfigValue_read_latency_2133:
|
||||
return pretty ? "2133 Read Latency" : "read_latency_2133";
|
||||
|
||||
case KipConfigValue_write_latency_1333:
|
||||
return pretty ? "1333 Write Latency" : "write_latency_1333";
|
||||
case KipConfigValue_write_latency_1600:
|
||||
return pretty ? "1600 Write Latency" : "write_latency_1600";
|
||||
case KipConfigValue_write_latency_1866:
|
||||
return pretty ? "1866 Write Latency" : "write_latency_1866";
|
||||
case KipConfigValue_write_latency_2133:
|
||||
return pretty ? "2133 Write Latency" : "write_latency_2133";
|
||||
|
||||
case KipConfigValue_mem_burst_read_latency:
|
||||
return pretty ? "Memory Burst Read Latency" : "mem_burst_read_latency";
|
||||
case KipConfigValue_mem_burst_write_latency:
|
||||
@@ -395,7 +475,7 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
|
||||
case KipConfigValue_g_volt_e_998400: return pretty ? "Erista GPU Volt 998 MHz" : "g_volt_e_998400";
|
||||
case KipConfigValue_g_volt_e_1036800: return pretty ? "Erista GPU Volt 1036 MHz" : "g_volt_e_1036800";
|
||||
case KipConfigValue_g_volt_e_1075200: return pretty ? "Erista GPU Volt 1075 MHz" : "g_volt_e_1075200";
|
||||
case KipConfigValue_t6_tRTW_fine_tune: return pretty ? "t6 - tRTW Fine Tune" : "t6_tRTW_fine_fune";
|
||||
case KipConfigValue_t6_tRTW_fine_tune: return pretty ? "t6 - tRTW Fine Tune" : "t6_tRTW_fine_tune";
|
||||
case KipConfigValue_t7_tWTR_fine_tune: return pretty ? "t7 - tWTR Fine Tune" : "t7_tWTR_fine_tune";
|
||||
case KipCrc32:
|
||||
return pretty ? "CRC32" : "crc32";
|
||||
@@ -406,25 +486,28 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
|
||||
static inline uint64_t hocclkDefaultConfigValue(HocClkConfigValue val)
|
||||
{
|
||||
switch(val)
|
||||
{
|
||||
case SysClkConfigValue_PollingIntervalMs:
|
||||
case HocClkConfigValue_PollingIntervalMs:
|
||||
return 300ULL;
|
||||
case SysClkConfigValue_TempLogIntervalMs:
|
||||
case SysClkConfigValue_FreqLogIntervalMs:
|
||||
case SysClkConfigValue_PowerLogIntervalMs:
|
||||
case SysClkConfigValue_CsvWriteIntervalMs:
|
||||
case HocClkConfigValue_TempLogIntervalMs:
|
||||
case HocClkConfigValue_FreqLogIntervalMs:
|
||||
case HocClkConfigValue_PowerLogIntervalMs:
|
||||
case HocClkConfigValue_CsvWriteIntervalMs:
|
||||
case HocClkConfigValue_UncappedClocks:
|
||||
case HocClkConfigValue_OverwriteBoostMode:
|
||||
case HorizonOCConfigValue_BatteryChargeCurrent:
|
||||
case HorizonOCConfigValue_OverwriteRefreshRate:
|
||||
case HorizonOCConfigValue_EnableUnsafeDisplayFreqs:
|
||||
case HorizonOCConfigValue_GPUScheduling:
|
||||
case HorizonOCConfigValue_LiveCpuUv:
|
||||
case HorizonOCConfigValue_GPUSchedulingMethod:
|
||||
case HocClkConfigValue_BatteryChargeCurrent:
|
||||
case HocClkConfigValue_OverwriteRefreshRate:
|
||||
case HocClkConfigValue_GPUScheduling:
|
||||
case HocClkConfigValue_LiveCpuUv:
|
||||
case HocClkConfigValue_GPUSchedulingMethod:
|
||||
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
|
||||
case HocClkConfigValue_MarikoMiddleFreqs:
|
||||
return 0ULL;
|
||||
case HocClkConfigValue_RamDisplayUnit:
|
||||
return (uint64_t)RamDisplayUnit_MHz;
|
||||
case HocClkConfigValue_EristaMaxCpuClock:
|
||||
return 1785ULL;
|
||||
|
||||
@@ -434,7 +517,7 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
|
||||
case HocClkConfigValue_ThermalThrottle:
|
||||
case HocClkConfigValue_HandheldTDP:
|
||||
case HocClkConfigValue_IsFirstLoad:
|
||||
case HorizonOCConfigValue_DVFSMode:
|
||||
case HocClkConfigValue_DVFSMode:
|
||||
return 1ULL;
|
||||
case HocClkConfigValue_ThermalThrottleThreshold:
|
||||
return 70ULL;
|
||||
@@ -442,12 +525,20 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
|
||||
return 9600ULL; // 8600mW will trigger on erista stock, so raise it a bit
|
||||
case HocClkConfigValue_LiteTDPLimit:
|
||||
return 6400ULL; // 0.5C
|
||||
case HocClkConfigValue_CpuGovernorMinimumFreq:
|
||||
return 612000000ULL; // 612MHz
|
||||
case HocClkConfigValue_MaxDisplayClockH:
|
||||
return 60ULL;
|
||||
case HocClkConfigValue_DisplayVoltage:
|
||||
return 1200ULL; // Auto
|
||||
case HocClkConfigValue_AulaDisplayColorPreset:
|
||||
return AulaDisplayColorMode_DoNotOverride;
|
||||
default:
|
||||
return 0ULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t input)
|
||||
static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t input)
|
||||
{
|
||||
switch(val)
|
||||
{
|
||||
@@ -456,23 +547,24 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
|
||||
case HocClkConfigValue_ThermalThrottleThreshold:
|
||||
case HocClkConfigValue_HandheldTDPLimit:
|
||||
case HocClkConfigValue_LiteTDPLimit:
|
||||
case SysClkConfigValue_PollingIntervalMs:
|
||||
case HocClkConfigValue_PollingIntervalMs:
|
||||
case HocClkConfigValue_MaxDisplayClockH:
|
||||
return input > 0;
|
||||
|
||||
case SysClkConfigValue_TempLogIntervalMs:
|
||||
case SysClkConfigValue_FreqLogIntervalMs:
|
||||
case SysClkConfigValue_PowerLogIntervalMs:
|
||||
case SysClkConfigValue_CsvWriteIntervalMs:
|
||||
case HocClkConfigValue_TempLogIntervalMs:
|
||||
case HocClkConfigValue_FreqLogIntervalMs:
|
||||
case HocClkConfigValue_PowerLogIntervalMs:
|
||||
case HocClkConfigValue_CsvWriteIntervalMs:
|
||||
case HocClkConfigValue_UncappedClocks:
|
||||
case HocClkConfigValue_OverwriteBoostMode:
|
||||
case HocClkConfigValue_ThermalThrottle:
|
||||
case HocClkConfigValue_HandheldTDP:
|
||||
case HorizonOCConfigValue_OverwriteRefreshRate:
|
||||
case HorizonOCConfigValue_EnableUnsafeDisplayFreqs:
|
||||
case HocClkConfigValue_OverwriteRefreshRate:
|
||||
case HocClkConfigValue_IsFirstLoad:
|
||||
case HorizonOCConfigValue_EnableExperimentalSettings:
|
||||
case HorizonOCConfigValue_LiveCpuUv:
|
||||
case HorizonOCConfigValue_GPUSchedulingMethod:
|
||||
case HocClkConfigValue_EnableExperimentalSettings:
|
||||
case HocClkConfigValue_LiveCpuUv:
|
||||
case HocClkConfigValue_GPUSchedulingMethod:
|
||||
case HocClkConfigValue_MarikoMiddleFreqs:
|
||||
return (input & 0x1) == input;
|
||||
|
||||
case KipConfigValue_custRev:
|
||||
@@ -482,9 +574,11 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
|
||||
case KipConfigValue_eristaEmcMaxClock:
|
||||
case KipConfigValue_eristaEmcMaxClock1:
|
||||
case KipConfigValue_eristaEmcMaxClock2:
|
||||
case KipConfigValue_stepMode:
|
||||
case KipConfigValue_marikoEmcMaxClock:
|
||||
case KipConfigValue_marikoEmcVddqVolt:
|
||||
case KipConfigValue_emcDvbShift:
|
||||
case KipConfigValue_marikoSocVmax:
|
||||
case KipConfigValue_t1_tRCD:
|
||||
case KipConfigValue_t2_tRP:
|
||||
case KipConfigValue_t3_tRAS:
|
||||
@@ -493,6 +587,18 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
|
||||
case KipConfigValue_t6_tRTW:
|
||||
case KipConfigValue_t7_tWTR:
|
||||
case KipConfigValue_t8_tREFI:
|
||||
case KipConfigValue_timingEmcTbreak:
|
||||
case KipConfigValue_low_t6_tRTW:
|
||||
case KipConfigValue_low_t7_tWTR:
|
||||
case KipConfigValue_t2_tRP_cap:
|
||||
case KipConfigValue_read_latency_1333:
|
||||
case KipConfigValue_read_latency_1600:
|
||||
case KipConfigValue_read_latency_1866:
|
||||
case KipConfigValue_read_latency_2133:
|
||||
case KipConfigValue_write_latency_1333:
|
||||
case KipConfigValue_write_latency_1600:
|
||||
case KipConfigValue_write_latency_1866:
|
||||
case KipConfigValue_write_latency_2133:
|
||||
case KipConfigValue_mem_burst_read_latency:
|
||||
case KipConfigValue_mem_burst_write_latency:
|
||||
case KipConfigValue_eristaCpuUV:
|
||||
@@ -569,12 +675,20 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
|
||||
case KipConfigValue_t6_tRTW_fine_tune:
|
||||
case KipConfigValue_t7_tWTR_fine_tune:
|
||||
case KipCrc32:
|
||||
case HorizonOCConfigValue_DVFSMode:
|
||||
case HorizonOCConfigValue_DVFSOffset:
|
||||
case HorizonOCConfigValue_GPUScheduling:
|
||||
case HocClkConfigValue_DVFSMode:
|
||||
case HocClkConfigValue_DVFSOffset:
|
||||
case HocClkConfigValue_GPUScheduling:
|
||||
case HocClkConfigValue_RAMVoltDisplayMode:
|
||||
case HocClkConfigValue_CpuGovernorMinimumFreq:
|
||||
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
|
||||
case HocClkConfigValue_RamDisplayUnit:
|
||||
case HocClkConfigValue_AulaDisplayColorPreset:
|
||||
return true;
|
||||
case HorizonOCConfigValue_BatteryChargeCurrent:
|
||||
case HocClkConfigValue_BatteryChargeCurrent:
|
||||
return ((input >= 1024) && (input <= 3072)) || !input;
|
||||
case HocClkConfigValue_DisplayVoltage:
|
||||
return ((input >= 800) && (input <= 1325));
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -27,13 +27,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define SYSCLK_ERROR_MODULE 388
|
||||
#define SYSCLK_ERROR(desc) ((SYSCLK_ERROR_MODULE & 0x1FF) | (SysClkError_##desc & 0x1FFF)<<9)
|
||||
#define HOCCLK_ERROR_MODULE 388
|
||||
#define HOCCLK_ERROR(desc) ((HOCCLK_ERROR_MODULE & 0x1FF) | (HocClkError_##desc & 0x1FFF)<<9)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SysClkError_Generic = 0,
|
||||
SysClkError_ConfigNotLoaded = 1,
|
||||
SysClkError_ConfigSaveFailed = 2,
|
||||
HocClkError_Generic = 0,
|
||||
HocClkError_ConfigNotLoaded = 1,
|
||||
HocClkError_ConfigSaveFailed = 2,
|
||||
// HocClkError_SocThermFail = 3,
|
||||
} SysClkError;
|
||||
} HocClkError;
|
||||
@@ -31,23 +31,23 @@
|
||||
#include "board.h"
|
||||
#include "clock_manager.h"
|
||||
|
||||
#define SYSCLK_IPC_API_VERSION 1
|
||||
#define SYSCLK_IPC_SERVICE_NAME "hoc:clk"
|
||||
#define HOCCLK_IPC_API_VERSION 2
|
||||
#define HOCCLK_IPC_SERVICE_NAME "hoc:clk"
|
||||
|
||||
enum SysClkIpcCmd
|
||||
enum HocClkIpcCmd
|
||||
{
|
||||
SysClkIpcCmd_GetApiVersion = 0,
|
||||
SysClkIpcCmd_GetVersionString = 1,
|
||||
SysClkIpcCmd_GetCurrentContext = 2,
|
||||
SysClkIpcCmd_Exit = 3,
|
||||
SysClkIpcCmd_GetProfileCount = 4,
|
||||
SysClkIpcCmd_GetProfiles = 5,
|
||||
SysClkIpcCmd_SetProfiles = 6,
|
||||
SysClkIpcCmd_SetEnabled = 7,
|
||||
SysClkIpcCmd_SetOverride = 8,
|
||||
SysClkIpcCmd_GetConfigValues = 9,
|
||||
SysClkIpcCmd_SetConfigValues = 10,
|
||||
SysClkIpcCmd_GetFreqList = 11,
|
||||
HocClkIpcCmd_GetApiVersion = 0,
|
||||
HocClkIpcCmd_GetVersionString = 1,
|
||||
HocClkIpcCmd_GetCurrentContext = 2,
|
||||
HocClkIpcCmd_Exit = 3,
|
||||
HocClkIpcCmd_GetProfileCount = 4,
|
||||
HocClkIpcCmd_GetProfiles = 5,
|
||||
HocClkIpcCmd_SetProfiles = 6,
|
||||
HocClkIpcCmd_SetEnabled = 7,
|
||||
HocClkIpcCmd_SetOverride = 8,
|
||||
HocClkIpcCmd_GetConfigValues = 9,
|
||||
HocClkIpcCmd_SetConfigValues = 10,
|
||||
HocClkIpcCmd_GetFreqList = 11,
|
||||
HocClkIpcCmd_SetKipData = 12,
|
||||
HocClkIpcCmd_GetKipData = 13,
|
||||
};
|
||||
@@ -56,17 +56,17 @@ enum SysClkIpcCmd
|
||||
typedef struct
|
||||
{
|
||||
uint64_t tid;
|
||||
SysClkTitleProfileList profiles;
|
||||
} SysClkIpc_SetProfiles_Args;
|
||||
HocClkTitleProfileList profiles;
|
||||
} HocClkIpc_SetProfiles_Args;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SysClkModule module;
|
||||
HocClkModule module;
|
||||
uint32_t hz;
|
||||
} SysClkIpc_SetOverride_Args;
|
||||
} HocClkIpc_SetOverride_Args;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SysClkModule module;
|
||||
HocClkModule module;
|
||||
uint32_t maxCount;
|
||||
} SysClkIpc_GetFreqList_Args;
|
||||
} HocClkIpc_GetFreqList_Args;
|
||||
11
Source/hoc-clk/common/include/hocclk/result.hpp
Normal file
11
Source/hoc-clk/common/include/hocclk/result.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <switch.h>
|
||||
|
||||
#define R_UNLESS(rc) \
|
||||
do { \
|
||||
if (R_FAILED(rc)) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* TODO: Add more Result macros. */
|
||||
756
Source/hoc-clk/common/include/ipc.h
Normal file
756
Source/hoc-clk/common/include/ipc.h
Normal file
@@ -0,0 +1,756 @@
|
||||
/**
|
||||
* @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
|
||||
}
|
||||
|
||||
///@}
|
||||
169
Source/hoc-clk/common/src/client/ipc.c
Normal file
169
Source/hoc-clk/common/src/client/ipc.c
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ INCLUDES := ../common/include
|
||||
EXEFS_SRC := exefs_src
|
||||
IS_MINIMAL := 0
|
||||
|
||||
APP_TITLE := Horizon OC Zeus
|
||||
APP_TITLE := Horizon OC Gaea
|
||||
NO_ICON := 1
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk
|
||||
# version control constants
|
||||
#---------------------------------------------------------------------------------
|
||||
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
|
||||
APP_VERSION := 0.40
|
||||
APP_VERSION := 2.3.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this
|
||||
TARGET_VERSION := $(APP_VERSION)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
@@ -49,12 +49,13 @@ DEFINES := -DDISABLE_IPC -DTARGET="\"$(TARGET)\"" -DTARGET_VERSION="\"$(TARGET_V
|
||||
|
||||
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||
|
||||
CFLAGS := -Os -Wall -flto -fdata-sections -ffunction-sections -fno-rtti -fno-common \
|
||||
CFLAGS := -O2 -Wall -flto -fdata-sections -ffunction-sections -fno-rtti -fno-common \
|
||||
$(ARCH) $(DEFINES)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||
|
||||
# Enable appearance overriding
|
||||
export MSYS2_ARG_CONV_EXCL := -DUI_OVERRIDE_PATH
|
||||
UI_OVERRIDE_PATH := /config/horizon-oc/
|
||||
CFLAGS += -DUI_OVERRIDE_PATH="\"$(UI_OVERRIDE_PATH)\""
|
||||
|
||||
@@ -63,7 +64,7 @@ CFLAGS += -DUI_OVERRIDE_PATH="\"$(UI_OVERRIDE_PATH)\""
|
||||
#CFLAGS += -DNO_FSTREAM_DIRECTIVE=$(NO_FSTREAM_DIRECTIVE)
|
||||
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++20
|
||||
CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++23
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
141
Source/hoc-clk/overlay/lang/de.json
Normal file
141
Source/hoc-clk/overlay/lang/de.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
141
Source/hoc-clk/overlay/lang/en.json
Normal file
141
Source/hoc-clk/overlay/lang/en.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"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 "
|
||||
}
|
||||
141
Source/hoc-clk/overlay/lang/es.json
Normal file
141
Source/hoc-clk/overlay/lang/es.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"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 "
|
||||
}
|
||||
141
Source/hoc-clk/overlay/lang/fr.json
Normal file
141
Source/hoc-clk/overlay/lang/fr.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
141
Source/hoc-clk/overlay/lang/it.json
Normal file
141
Source/hoc-clk/overlay/lang/it.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
141
Source/hoc-clk/overlay/lang/ja.json
Normal file
141
Source/hoc-clk/overlay/lang/ja.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"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 が原因となる可能性があります"
|
||||
}
|
||||
146
Source/hoc-clk/overlay/lang/jp.json
Normal file
146
Source/hoc-clk/overlay/lang/jp.json
Normal file
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
141
Source/hoc-clk/overlay/lang/ko.json
Normal file
141
Source/hoc-clk/overlay/lang/ko.json
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"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는 다음을 유발할 수 있습니다."
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user