726 Commits
0.29 ... main

Author SHA1 Message Date
ca25cff7fa Merge upstream Horizon-OC main (749fd385)
Some checks failed
Build Horizon OC Zeus / build (push) Failing after 6s
Includes 2.3.1 changes: kip migration, erista GPU workaround, safety
fixes, and Mariko GPU boot voltage patch. Resolve dist binary conflicts
with upstream build artifacts.
2026-05-25 20:54:23 +02:00
Lightos1
749fd385ce revert erista mrf changes 2026-05-24 21:30:05 +02:00
Lightos1
65927cffce Revert "Update misc_gui.cpp"
This reverts commit fae0fa5d5f.
2026-05-24 21:21:00 +02:00
souldbminersmwc
10bcf4542d hocclk: fix safety features 2026-05-24 12:34:37 -04:00
souldbminersmwc
afeee4937c Update horizon-oc-overlay.ovl 2026-05-23 20:09:35 -04:00
souldbminersmwc
66308b152b hocclk: kip migration 2026-05-23 20:07:28 -04:00
souldbminersmwc
72dfdb53fc hocclk: fix small issue 2026-05-23 19:45:47 -04:00
souldbminersmwc
ee730ca68e all: add mariko gpu boot voltage patch 2026-05-23 19:43:54 -04:00
souldbminersmwc
2d0a2c4f6d hocclk: fix edge case with middle freqs 2026-05-23 16:40:04 -04:00
souldbminersmwc
8411a14b26 hocclk: fix ui issue 2026-05-22 17:50:36 -04:00
souldbminersmwc
99e9270314 hocclk: add erista gpu workaround 2026-05-22 17:48:21 -04:00
souldbminersmwc
58488f7f48 chore: add no exosphere build scripts 2026-05-22 17:40:49 -04:00
souldbminersmwc
c2238eb070 hocclk: perfect migration between kip versions 2026-05-22 16:48:48 -04:00
souldbminersmwc
4271efb4a7 hocclk/ldr: add kip migration skeleton 2026-05-22 16:35:56 -04:00
souldbminersmwc
fd9e5a2e08 add more docs and warnings 2026-05-22 16:16:06 -04:00
souldbminersmwc
4587b01152 hocclk: minor configurator changes 2026-05-22 16:13:16 -04:00
souldbminersmwc
fae0fa5d5f Update misc_gui.cpp 2026-05-22 16:10:18 -04:00
souldbminersmwc
059fe40339 hocclk: update configurator for new features 2026-05-22 16:10:12 -04:00
Lightos1
a617a7398a Implement Mariko-style MRF for erista
There is heavy code duplication because I'm lazy, but loader needs to be
refactored anyway at some point and this "works" for now.
2026-05-22 21:57:32 +02:00
souldbminersmwc
2f092f7955 hocclk: fix UV0 with live cpu uv 2026-05-19 17:15:12 -04:00
souldbminersmwc
003854d2bb hocclk: update live cpu uv more often 2026-05-19 17:06:54 -04:00
souldbminersmwc
f49e1a80a0 hocclk: erista finally gets some love
bugfixes and live CPU uv fixed (for erista at least)
2026-05-19 17:04:14 -04:00
souldbminersmwc
16ea883fa8 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-05-19 16:22:06 -04:00
souldbminersmwc
f787ed7fd8 hocclk: fix tpyos 2026-05-19 16:22:03 -04:00
Lightos1
63c7fb211d Code cleanup and improved dvb tables.
Thanks to: jimmy for testing and b3711 for many additional voltage scale improvements.

Co-Authored-By: halop <4215938+halop@users.noreply.github.com>
2026-05-19 20:02:09 +02:00
Lightos1
52e13f5e1e Merge pull request #81 from IMLeoCat/patch-1
Update README.md
2026-05-18 10:32:13 +02:00
d59f0b5900 build: fix macOS compile and dist packaging
Some checks failed
Build Horizon OC Zeus / build (push) Failing after 4s
Use portable sed for title_id extraction, copy hoc-clk output into dist/
correctly, and ensure overlay output directories exist. Rebuild dist after
upstream merge.
2026-05-17 10:58:03 +02:00
5403c3bdd8 Merge upstream Horizon-OC main (797bcf79) 2026-05-17 10:52:03 +02:00
souldbminersmwc
797bcf79a2 hocclk: fix resolution read bug
thx masa for pointing this out
2026-05-16 15:50:32 -04:00
e962de8373 hocclk: translate user notifications to German
Some checks failed
Build Horizon OC Zeus / build (push) Failing after 1m16s
2026-05-16 11:52:08 +02:00
souldbminersmwc
0d227cfb82 hocclk: add info about new feature 2026-05-13 19:52:11 -04:00
souldbminersmwc
3010c915a7 hocclk: add info to hocclk 2026-05-13 19:49:48 -04:00
souldbminersmwc
196933a6b3 hocclk: read voltage from i2c instead of rgltr 2026-05-12 17:00:46 -04:00
Lightos1
bfc93c6238 ldr: don't be an idiot, remove debug code 2026-05-12 21:29:27 +02:00
Lightos1
6d284d9c2b ldr: fix dvb brackets 2026-05-12 21:26:42 +02:00
Lightos1
7384404ac1 ldr: use DvbMeta struct 2026-05-12 20:05:02 +02:00
IMLeoCat
35de44d3fa Update README.md
The Installation part is outdated. add this line from "https://horizon-oc.github.io/guide.html".
2026-05-13 01:13:30 +08:00
Lightos1
1b07df5f08 Revert "change cust rev back to 2, it shouldn't have been changed in the first place"
This reverts commit d6c2ddf2ea.
2026-05-12 13:40:35 +02:00
Lightos1
34e1f39510 ldr: even more (partially guessed) labeling of CvbMeta 2026-05-12 13:07:46 +02:00
Lightos1
ef830ce3f6 ldr: label more of CvbMeta 2026-05-12 13:03:51 +02:00
Lightos1
d7e22b3ccc ldr: add partially labeled CvbMeta struct 2026-05-12 12:44:52 +02:00
Lightos1
d6c2ddf2ea change cust rev back to 2, it shouldn't have been changed in the first place 2026-05-12 11:22:51 +02:00
Lightos1
b4627ad171 ldr: minVolt -> MinVolt 2026-05-12 10:34:05 +02:00
Lightos1
381a1eafc4 ldr: properly clamp soc voltage 2026-05-12 10:32:14 +02:00
souldbminersmwc
f90ba2ca59 2.3.0 2026-05-11 19:07:44 -04:00
Lightos1
f97fdc9528 conversion script: fix 3200mhz 2026-05-11 21:34:01 +02:00
Lightos1
ed63a95488 conversion script: fix 3200mhz 2026-05-11 21:33:44 +02:00
Lightos1
e24512c9d6 raise vddq min validator to 400 2026-05-11 21:26:31 +02:00
Lightos1
1efd91d535 reaise vddq validator 2026-05-11 21:22:05 +02:00
Lightos1
3337738dcc conversion script: fix silly bugs 2026-05-11 19:58:08 +02:00
Lightos1
7f1f504c36 Merge branch 'main' of https://github.com/horizon-OC/Horizon-OC 2026-05-11 19:36:10 +02:00
Lightos1
2f21f23266 experimental: add improved dvb table (+ conversion script) 2026-05-11 19:35:48 +02:00
Lightos1
3c99ad0186 Update README.md 2026-05-11 17:22:54 +02:00
Lightos1
e4dd690ac9 Update README.md 2026-05-11 17:19:36 +02:00
Lightos1
9122768953 delete ams_patch.bat from the right branch 2026-05-10 22:44:32 +02:00
Lightos1
3539916cfd fix compilation errors 2026-05-10 22:36:29 +02:00
Lightos1
4bd776c8aa kip.cpp: improve version mismatch logging 2026-05-10 18:58:37 +02:00
souldbminersmwc
78cda43054 whoops i forgot code 2026-05-10 12:48:02 -04:00
souldbminersmwc
580db1f167 hocclk: fix gm20b driver 2026-05-10 12:47:49 -04:00
souldbminersmwc
ea9adbfa14 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-05-10 11:22:33 -04:00
souldbminersmwc
accfea86f1 hocmon: get real freqs from hocclk instead of clkrst 2026-05-10 11:22:31 -04:00
Lightos1
badf182529 build.sh: improve build process 2026-05-10 01:51:22 +02:00
souldbminersmwc
12f12c6b1e hocclk: fix cust rev detection 2026-05-09 19:13:05 -04:00
souldbminersmwc
dde723baee Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-05-09 19:04:44 -04:00
souldbminersmwc
27db54644e hocclk: fix 1267mhz clock voltage 2026-05-09 19:04:41 -04:00
Lightos1
138bcb6dc6 build.sh: alternatively I should consider not being stupid 2026-05-10 00:57:31 +02:00
Lightos1
05fa5034d3 Revert "build.sh: use make -j rather than -j8"
This reverts commit 8a98a57b4b.
Thansk to microslops amazing memory management, make -j sometimes runs
out of ram on my system when compiling libstratosphere. This can hang my
entire system and also corrupt ram??? WTF
2026-05-10 00:44:27 +02:00
Lightos1
8a98a57b4b build.sh: use make -j rather than -j8 2026-05-10 00:31:26 +02:00
souldbminersmwc
c12b0136f8 hocclk: add missing code and small change to overlay 2026-05-09 16:54:39 -04:00
souldbminersmwc
c4f7f0e713 hocclk: add mariko middle freq hack 2026-05-09 16:53:30 -04:00
Lightos1
935dd24129 move exosphere to /dist/Atmosphere/ 2026-05-09 22:51:57 +02:00
Lightos1
d72e05259a Update COMPILING.md 2026-05-09 22:46:33 +02:00
Lightos1
847ae19de9 add hoc notification icon -- thanks ppkantorski! 2026-05-09 22:44:35 +02:00
souldbminersmwc
8651e418ca Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-05-09 16:07:03 -04:00
souldbminersmwc
2e3ef0f3b0 add GM20B GPU clock driver 2026-05-09 16:06:57 -04:00
Lightos1
c018b10a7f soctherm: remove reduntant EnableSensor calls 2026-05-09 21:41:11 +02:00
souldbminersmwc
336ea0eddb hocclk: fix me being a idiot 2026-05-09 15:37:24 -04:00
Lightos1
d561c0e538 add speedo fuse reading and fix wafer coordinates 2026-05-09 14:20:43 +02:00
Lightos1
a7c1fe0d27 ldr: clean formating, fix tRC cap, map erista mtc indexes 2026-05-09 13:39:31 +02:00
Souldbminer
33582678c2 Update README.md 2026-05-08 22:44:06 -04:00
souldbminersmwc
3ca1f17e4d hocclk: major code refactor
move everything into its own directory, clean codebase up a lot
2026-05-08 22:43:14 -04:00
souldbminersmwc
65dfa8f48a Update board_fuse.cpp 2026-05-08 16:38:46 -04:00
souldbminersmwc
90ea18c881 hocclk: better HW info reading 2026-05-08 16:36:49 -04:00
souldbminersmwc
598136c64b hocclk: fixes and other stuff 2026-05-08 15:53:53 -04:00
Lightos1
848a788c5f add kip version detection + version mismatch warning 2026-05-08 20:44:28 +02:00
Lightos1
d443e069fd add more stable max index checks 2026-05-08 18:39:01 +02:00
Lightos1
b7e7f0f720 clarify stable struct 2026-05-08 18:18:32 +02:00
Lightos1
916e5c5ddf add stable max index checks 2026-05-08 18:10:32 +02:00
Lightos1
c766ab1569 add stable struct to HocClkContext 2026-05-08 17:58:27 +02:00
Lightos1
9ee4d79f77 Merge pull request #78 from dominatorul/patch-7
Enhance README with more MEM clock entries
2026-05-07 22:41:37 +02:00
Lightos1
d55d1ab319 Merge pull request #77 from th3-ne0undr5c0r/fr,-fr-CA
Add proper French support
2026-05-07 22:39:18 +02:00
Dominator
3be1eb6149 Enhance README with more MEM clock entries
Added additional memory clock values and clarified JEDEC standards.
2026-05-07 19:57:33 +03:00
th3-ne0undr5c0r
5e4f58975c Remove fr-ca support
remove French Canadian support as it is not supported.
2026-05-07 09:31:28 -06:00
th3-ne0undr5c0r
d0c257dbc0 Add proper French support
As title says, adds proper support for French. please keep in mind I have not yet tested/checked whether or not text clipping or formatting issues may be present.
2026-05-07 09:27:38 -06:00
Lightos1
ffb0ded210 Merge pull request #76 from redraz/patch-1
Update ru.json
2026-05-07 16:48:51 +02:00
redraz
396304c738 Update ru.json
I decided to make a proper translation into Russian
2026-05-07 17:04:22 +03:00
Lightos1
8c6baf9e2f add hocVersion to CustomizeTable in sysmodule 2026-05-06 08:56:25 +02:00
Lightos1
a24eec7256 add versioning to kip 2026-05-06 08:54:23 +02:00
Lightos1
77a45966da Revert cust change
This reverts commit 04d7e2f8e0.
2026-05-06 08:49:31 +02:00
Lightos1
04d7e2f8e0 Change cust rev
This now uses the minimum hoc-clk version the kip is compatible with.
2026-05-06 08:43:33 +02:00
souldbminersmwc
f5481d1c00 hocclk: dvfs bracket fix - thanks jotommy! 2026-05-05 15:41:42 -04:00
Lightos1
2e39421074 Refactor tsensor 2026-05-05 20:43:43 +02:00
souldbminersmwc
9cf901d487 Update aotag.hpp 2026-05-04 14:56:48 -04:00
souldbminersmwc
0cdc96f7f8 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-05-04 14:28:26 -04:00
souldbminersmwc
73fafa6337 aotag: fix nvidia Z
nvidia made a error with their driver somehow
2026-05-04 14:28:24 -04:00
Lightos1
cfae441656 fix boost mode exit 2026-05-04 18:33:38 +02:00
souldbminersmwc
bf6bfa3e57 aotag: calculate (hopefully) correct coeffs 2026-05-03 15:44:31 -04:00
souldbminersmwc
72f5e11c42 update aotag coeffs 2026-05-03 14:41:37 -04:00
souldbminersmwc
93b6974be5 Update aotag.cpp 2026-05-03 14:14:14 -04:00
th3-ne0undr5c0r
1f545a7030 Add French Canadian language support
As title says, adds support for French Canadian. please keep in mind I have not yet tested/checked whether or not text clipping or formatting issues may be present.
2026-05-02 23:13:51 -06:00
souldbminersmwc
73bcb384b5 add exosphere patch 2026-05-02 22:11:25 -04:00
souldbminersmwc
fab0b76a35 update binaries 2026-05-02 22:06:57 -04:00
souldbminersmwc
2f830bac53 hocclk: better aotag 2026-05-02 22:05:15 -04:00
souldbminersmwc
5bf7fdf60e Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-05-02 21:11:56 -04:00
souldbminersmwc
af4f00f682 hocclk: basic aotag support along with other changes 2026-05-02 21:11:54 -04:00
Lightos1
d4b09f147b add back emc data that I forgot 2026-05-02 16:17:09 +02:00
Lightos1
1b33e9982c update exosphere patches 2026-05-02 16:16:21 +02:00
souldbminersmwc
c998aa78ae hocclk: update credits 2026-05-02 10:12:42 -04:00
Souldbminer
2b5a27f86e Update README.md 2026-05-02 10:11:14 -04:00
Lightos1
52b1b8da81 fix gpu volt hijack logic 2026-05-01 22:49:19 +02:00
souldbminersmwc
f409f5b8c3 hocclk: dvfs logic refactor
make it a lot more stable
2026-04-30 19:58:48 -04:00
souldbminersmwc
77a7470149 fix clock reset bug
Co-Authored-By: ppkantorski <6467366+ppkantorski@users.noreply.github.com>
2026-04-30 19:36:10 -04:00
souldbminersmwc
1d9fa4c091 Update t210.c 2026-04-30 19:29:15 -04:00
souldbminersmwc
a16cf8a2ef hocclk: significantly reduce memory usage 2026-04-30 19:15:05 -04:00
souldbminersmwc
89418b7cd0 hocclk: some refactoring and commenting 2026-04-30 18:48:57 -04:00
Lightos1
00cf05d9c5 ldr: slightly clean up MemFreqMtcTable 2026-04-29 22:14:27 +02:00
Lightos1
39e7ba6b75 ldr: move R_SKIP after verifying mtc table to avoid triggering patch validations 2026-04-29 21:56:25 +02:00
Lightos1
d7f2cee3b1 Update README.md 2026-04-29 19:08:23 +02:00
Lightos1
3848bd7c83 Update README.md 2026-04-29 19:06:51 +02:00
Lightos1
693954930a remove oc_test 2026-04-29 18:55:14 +02:00
Lightos1
de89768c74 ldr:: panic::SmcError is now always included 2026-04-29 18:39:20 +02:00
Lightos1
8f243c8369 ldr: clean up includes 2026-04-29 18:35:42 +02:00
souldbminersmwc
fd91f376c4 bump version 2026-04-27 19:33:20 -04:00
souldbminersmwc
f0fe114303 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-27 19:23:47 -04:00
souldbminersmwc
1c64cf5db8 hocclk: add display color option for aula
this is very very subtle but can def make a difference to your experience
2026-04-27 19:23:44 -04:00
Lightos1
9eea39325d Update about_gui.cpp 2026-04-27 08:52:54 +02:00
Lightos1
9bb6b3383b Update README.md 2026-04-27 08:51:57 +02:00
Lightos1
fb3e976f2e ldr: fix 1333 latency max bug -- index 0 is a valid latency 2026-04-26 22:13:49 +02:00
Lightos1
ec4a1f974b variable should be lowercase 2026-04-25 23:15:39 +02:00
souldbminersmwc
fc4b3b8352 2.1.0 2026-04-25 16:40:55 -04:00
souldbminersmwc
5123f7b1de Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-25 16:39:31 -04:00
souldbminersmwc
3251d104c4 hocclk: rename UV tables to correct names 2026-04-25 16:30:10 -04:00
Lightos1
ea2d604c20 fix comment 2026-04-25 22:22:31 +02:00
souldbminersmwc
ae71cd447d ldr: add comment 2026-04-25 16:19:44 -04:00
souldbminersmwc
4261dd83f0 hoc: 133mhz step mode 2026-04-25 16:16:26 -04:00
souldbminersmwc
4dc01c024a hocclk: change capitilization 2026-04-25 15:59:05 -04:00
souldbminersmwc
9e26329173 hocclk: soc max volt dno side voltage 2026-04-25 15:57:37 -04:00
souldbminersmwc
36441e6dea add config support and update build script for soc vmax 2026-04-25 15:44:56 -04:00
Lightos1
cdf28607c9 move loader.json 2026-04-25 20:48:31 +02:00
Lightos1
b383fa7ec2 improve alignment 2026-04-25 20:30:46 +02:00
Lightos1
d7178ddd2c Add soc uncap 2026-04-25 20:22:23 +02:00
souldbminersmwc
9d149f0939 hocclk: fix trackbar reloading issues 2026-04-25 11:20:48 -04:00
souldbminersmwc
170dd79347 hocclk: cpu config rework 2026-04-25 11:06:22 -04:00
souldbminersmwc
d97d359ec2 hocclk: more configurator stuff 2026-04-24 17:00:59 -04:00
souldbminersmwc
9ff9e315a0 hocclk: improve kip saving mechanism 2026-04-24 16:42:59 -04:00
souldbminersmwc
cde560c4bd Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-24 16:35:25 -04:00
souldbminersmwc
a15860ba8d hocclk: make dram ti 2026-04-24 16:35:01 -04:00
Lightos1
76e6f1bd27 Merge pull request #71 from dominatorul/patch-4
Update README.md
2026-04-24 17:41:41 +02:00
Lightos1
2c1ad1b755 fix formating 2026-04-24 17:41:21 +02:00
Lightos1
2f2e5e9fb8 add new line 2026-04-24 17:38:28 +02:00
Lightos1
65b77a2d0b make step mode mariko only 2026-04-24 17:34:37 +02:00
Dominator
5d4812bde5 Update README.md
Updated clock specifications for MEM, CPU, and GPU with standardized terms.
2026-04-24 04:24:03 +03:00
souldbminersmwc
addd2de4d9 hocclk: add CPU LUT and add copyright 2026-04-23 18:50:51 -04:00
souldbminersmwc
0df8ce745f use original voltages 2026-04-22 18:06:27 -04:00
souldbminersmwc
62d055f163 hocclk: add missing -35mV step to configurator 2026-04-22 16:58:36 -04:00
souldbminersmwc
fa1510a40d Add B3's low ram freq DVFS
Co-Authored-By: halop <4215938+halop@users.noreply.github.com>
2026-04-22 16:56:09 -04:00
souldbminersmwc
e6942f95e7 hocclk: add BQ24193 temp driver 2026-04-22 16:40:51 -04:00
souldbminersmwc
94b63003ab hocclk: reorder options 2026-04-22 16:14:30 -04:00
souldbminersmwc
8aa20dc0cf hocclk: fix PLL ram mode and default to it 2026-04-22 15:49:07 -04:00
souldbminersmwc
db92c60cd2 hocclk: improve pll ram mode 2026-04-21 20:03:12 -04:00
souldbminersmwc
c8c7b233f5 hocclk: minor refactor 2026-04-21 19:52:31 -04:00
souldbminersmwc
5a9afcbb74 hocmon: rename tabs 2026-04-21 19:48:42 -04:00
souldbminersmwc
8127a0c3b7 hocclk: dram voltage color changes for mariko/erista 2026-04-21 19:48:27 -04:00
souldbminersmwc
7c5e746594 hocclk: ui changes 2026-04-21 19:46:21 -04:00
souldbminersmwc
7d30c4d384 bump version 2026-04-21 19:38:32 -04:00
souldbminersmwc
883b4fc3f4 hocclk: fix pointer dereference 2026-04-21 19:35:05 -04:00
Lightos1
530588a818 bump version 2026-04-21 21:29:18 +02:00
Lightos1
a6402bd5d5 update startPtr AFTER PrepareMtcMemoryRegion 2026-04-21 21:28:18 +02:00
Lightos1
cd3d29ce88 I'm a fucking idiot... remove debug code 2026-04-21 09:58:11 +02:00
souldbminersmwc
192b70dae4 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-20 19:19:44 -04:00
souldbminersmwc
aaa9f90794 hocclk: console specific changes 2026-04-20 19:19:40 -04:00
Lightos1
d5e38b0eb3 Update SECURITY.md 2026-04-20 21:44:44 +02:00
Lightos1
3704d46136 set default step mode to 66MHz 2026-04-20 21:30:02 +02:00
Lightos1
b374117c37 properly fix time stuff - thanks masa! 2026-04-20 20:14:29 +02:00
Lightos1
c9c5d08919 add updated spanish translation by tdrr 2026-04-20 19:57:24 +02:00
Lightos1
c963dd8369 fix auto latency according to spec 2026-04-20 15:18:14 +02:00
Lightos1
7fc9682de0 use max freq in jedec mode regardless of it being jedec or not 2026-04-20 15:06:14 +02:00
Lightos1
0c555f34c3 remove (failing) cust rev check 2026-04-20 14:54:37 +02:00
Lightos1
fb37d5f0e7 reduce context size 2026-04-20 14:37:36 +02:00
Lightos1
a7619c39d2 set read/write latency to 1600 cause it's only used on erista 2026-04-20 07:31:52 +02:00
souldbminersmwc
55b97156ce hocclk: improve kip logging and add failsafe 2026-04-19 19:16:51 -04:00
souldbminersmwc
37ad65e768 final hoc 2.0.0 changes 2026-04-19 19:15:10 -04:00
souldbminersmwc
3b4877b287 update submodules 2026-04-19 15:38:07 -04:00
Lightos1
01d79aab30 fix off by one 2026-04-19 19:57:58 +02:00
Lightos1
3bb7b4cbd5 change colors 2026-04-19 19:17:36 +02:00
Lightos1
9c16f8b2b8 fix typo 2026-04-19 16:12:57 +02:00
Lightos1
78112547b6 prevent switch from combusting into 4 billion volts :D 2026-04-19 15:47:46 +02:00
Lightos1
3c7a42b033 formating 2026-04-19 15:13:35 +02:00
Lightos1
203587523a change result 2026-04-19 15:13:19 +02:00
Lightos1
07d0824c55 Merge pull request #66 from Horizon-OC/mrf-but-no-z-with-z
mrf
2026-04-19 15:07:02 +02:00
Lightos1
f1eab00ce1 revert custom and pcv to default settings 2026-04-19 14:51:15 +02:00
Lightos1
6a851d4095 nso start check, timing tbreak and some horrendous ui code 2026-04-18 23:00:21 +02:00
Lightos1
822e9f2817 add t2 trp cap 2026-04-18 19:02:07 +02:00
Lightos1
55b3a4230c Remove useless comments, yes I cannot be bothered to write ui code myself :D; fuck ui 2026-04-18 16:31:49 +02:00
Lightos1
be61b9df0e add sloppy configurator: todo fix crap 2026-04-18 16:30:24 +02:00
Lightos1
6c8d429c64 add missing configs 2026-04-18 12:21:42 +02:00
Lightos1
aa95f526b8 add mrf latency stuff to configs 2026-04-18 12:19:55 +02:00
Lightos1
638ddb499c add jedec mode 2026-04-18 11:52:06 +02:00
Lightos1
c95b6fde88 assign all mtc tables 2026-04-17 23:40:17 +02:00
Lightos1
2dd726723e add latency switching 2026-04-17 20:26:32 +02:00
Lightos1
119f49a3a4 fix freq list generation and dvb 2026-04-17 16:36:12 +02:00
Lightos1
51f935c252 initial mrf implementation with some bugs to fix 2026-04-16 20:51:26 +02:00
Lightos1
e1003520eb Revert "initial mrf implementation with some bugs to fix"
This reverts commit 819913ffd9.
2026-04-16 20:49:58 +02:00
Lightos1
3b5185df8d Reapply "Partial workflow fixes? (#63)"
This reverts commit 26c1dd1f99.
2026-04-16 20:49:42 +02:00
Lightos1
26c1dd1f99 Revert "Partial workflow fixes? (#63)"
This reverts commit 923bd0c0ba.
2026-04-16 20:49:11 +02:00
Lightos1
819913ffd9 initial mrf implementation with some bugs to fix 2026-04-16 20:48:22 +02:00
Lightos1
923bd0c0ba Partial workflow fixes? (#63)
* Update build.yml

* Update build.yml

* Update build.yml
2026-04-13 16:53:25 +02:00
Lightos1
185e5bcf31 Update build.yml 2026-04-13 15:52:20 +02:00
souldbminersmwc
d1ef905ac5 hocmon: real temps fixes 2026-04-12 20:56:39 -04:00
souldbminersmwc
fad9b5be50 hocmon: fix vdd2/vddq flip and full/mini mode 2026-04-12 20:06:23 -04:00
souldbminersmwc
c6424403b3 hocmon: partially add ram bw 2026-04-12 17:43:45 -04:00
souldbminersmwc
2b23498285 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-12 16:53:01 -04:00
souldbminersmwc
c7db7a7632 hocclk: fix compilation 2026-04-12 16:51:52 -04:00
Lightos1
985ecdc980 add fatal handler payload 2026-04-12 22:04:19 +02:00
Lightos1
e1d75f0084 add oc_log files 2026-04-12 22:00:45 +02:00
souldbminersmwc
f276ca0187 hocclk: rename mem display unit to ram display unit 2026-04-11 21:05:28 -04:00
souldbminersmwc
ae39b1b1bd hocclk: fix MT/s display on 1600mhz 2026-04-11 21:03:31 -04:00
souldbminersmwc
9321aed1d1 bump version 2026-04-11 20:58:22 -04:00
souldbminersmwc
756c44ba82 hocclk: fix compile errors 2026-04-11 20:57:28 -04:00
souldbminersmwc
037f011c9b hocclk: peak emc bw and mem display unit option
also correct bw reading to keep it accurate
2026-04-11 20:56:35 -04:00
souldbminersmwc
d2a46e1202 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-11 19:08:19 -04:00
souldbminersmwc
976f133c97 add MT/S to mem profiles 2026-04-11 19:08:12 -04:00
Lightos1
accdf6dc34 fix Z - thanks masa! 2026-04-12 01:05:11 +02:00
souldbminersmwc
b417099a4a hocclk: add RAM bandwidth monitor 2026-04-11 19:00:22 -04:00
souldbminersmwc
b7440a38a5 hocclk: reduce memory usage for real this time 2026-04-11 18:25:30 -04:00
souldbminersmwc
6369382de1 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-11 17:59:20 -04:00
souldbminersmwc
65db0b4989 hocclk: 800mV minimum display voltage 2026-04-11 17:59:18 -04:00
Lightos1
19b29b97e1 separate pcv.hpp into erista/mariko files 2026-04-10 17:45:32 +02:00
Lightos1
ec230e35d0 make this pretty 2026-04-09 11:33:44 +02:00
Lightos1
5942cfd68d ldr: refactor asm 2026-04-09 11:32:00 +02:00
souldbminersmwc
66d0109a9a hocclk: autosave kip and reduce uv3 to 675 2026-04-08 19:15:26 -04:00
souldbminersmwc
6334603f9e hocclk: undo alignment
this doesnt help at all, it makes it less aligned
2026-04-08 17:51:20 -04:00
souldbminersmwc
5d12c1721a hocclk: reorganize about section 2026-04-08 17:49:00 -04:00
Lightos1
7e42394894 fix GpuClkOsLimit name 2026-04-08 22:17:28 +02:00
Lightos1
07dd65eebf move GpuClkOsLimit to common 2026-04-08 22:15:52 +02:00
Lightos1
ca8bb25660 add GpuOsLimit verification to mariko; erista will be pushed once verified 2026-04-08 21:30:36 +02:00
souldbminersmwc
90e53b52b2 hocclk: refactoring and ram pll measurement 2026-04-07 19:48:17 -04:00
Lightos1
c6cd863526 Bump ams version 2026-04-07 19:45:16 +02:00
Lightos1
24eb3b1208 recover alignment improvement commit changes 2026-04-07 19:32:37 +02:00
Lightos1
9a6282af9c bump version 2026-04-07 19:20:52 +02:00
Lightos1
5de030c4e9 rename globals 2026-04-06 23:23:04 +02:00
souldbminersmwc
dc63dfdca2 hocclk: add support for FW <10.0.0 2026-04-06 16:52:18 -04:00
souldbminersmwc
803a838b35 Update Utils.hpp 2026-04-06 16:10:02 -04:00
souldbminersmwc
5b284db5ae hocmon: lower default font size 2026-04-06 16:09:59 -04:00
souldbminersmwc
861ee85289 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-06 15:57:12 -04:00
souldbminersmwc
521e3c22dd hocclk: reduce ram usage and add display undervolt 2026-04-06 15:57:10 -04:00
Lightos1
78798a1cce actually fix cpu max 2026-04-06 12:58:54 +02:00
Lightos1
44d417fb89 fix cpu max clock, remove below 1963 caps on mariko 2026-04-06 12:56:06 +02:00
Lightos1
8faaaa58d9 mini: fix garbage temp reading 2026-04-04 12:15:09 +02:00
Lightos1
df0969ba6f update readme 2026-04-04 11:59:10 +02:00
Lightos1
646f8b8d69 update readme 2026-04-04 11:58:44 +02:00
Lightos1
456684152a more accurate Italian translation by miki 2026-04-04 11:56:10 +02:00
souldbminersmwc
a606a878ee hocclk: fix lightos z 2026-04-03 20:02:36 -04:00
souldbminersmwc
75531e6c4f add ldr patches for older atmosphere versions (in case anyone still uses them) 2026-04-03 19:42:29 -04:00
souldbminersmwc
fa7313c495 remove real temp decimals 2026-04-03 19:29:57 -04:00
souldbminersmwc
5816d49f68 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-03 19:25:22 -04:00
souldbminersmwc
589c41d8e5 hocmon: fix vdd2/vddq order 2026-04-03 19:25:14 -04:00
Lightos1
b35971e265 bump ams version 2026-04-04 01:24:51 +02:00
Lightos1
1d3836fcbb ams 1.11 is now the latest version 2026-04-04 01:22:41 +02:00
Lightos1
e515df84a5 prepare ldr_process_creation for ams 1.11.0 2026-04-04 01:17:09 +02:00
souldbminersmwc
901cb15191 hocclk: fix nvcheck sched 2026-04-03 15:12:33 -04:00
souldbminersmwc
aaa00ca3d0 hocclk: add negatives before gpu offset 2026-04-03 12:39:49 -04:00
souldbminersmwc
1f449af24f hocclk: rename gpu uv options 2026-04-03 12:37:07 -04:00
souldbminersmwc
cb810367b8 hocclk: remove 800mv vmin option 2026-04-03 12:35:22 -04:00
souldbminersmwc
27cd40a7e8 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-03 12:34:17 -04:00
souldbminersmwc
75480ffdb5 hocclk: fix vrr out of focus 2026-04-03 12:34:15 -04:00
Lightos1
d3ee7c2976 remove auto vmin from configurator 2026-04-03 15:34:21 +02:00
Lightos1
3e5cec375d ldr: remove unused structs 2026-04-03 14:55:22 +02:00
Lightos1
280392158e ldr: deprecate auto vmin, it was incorrectly implemente, proper implementation is inferior to pcv hijack 2026-04-03 14:52:40 +02:00
Lightos1
a8d47ae8c8 Fix garbage temp reading 2026-04-03 13:55:31 +02:00
Lightos1
eb6364f994 add decimal config 2026-04-03 13:39:46 +02:00
Lightos1
59ba05332f micro mode: fix padding 2026-04-03 13:15:01 +02:00
souldbminersmwc
fc203c723c Update MIGRATION.md 2026-04-02 22:10:24 -04:00
souldbminersmwc
5f427ec24d Update MIGRATION.md 2026-04-02 22:10:00 -04:00
souldbminersmwc
cf891190cd hocmon: fix VDD2/VDDQ reversal 2026-04-02 22:09:11 -04:00
souldbminersmwc
3411959537 hocclk: add migration guide 2026-04-02 17:13:44 -04:00
souldbminersmwc
cce2069a32 hocclk: build system changes
- update C++ version to GNU23, change how build scripts behave and more
2026-04-02 16:59:11 -04:00
souldbminersmwc
485aa83de5 hocclk: remove sysclk licensing from hocclk code 2026-04-02 16:51:43 -04:00
souldbminersmwc
52e8f5c584 sysclk: rename to hocclk
idc about compatability when the programs are structured very differently, work very differently, and send/get data in very different ways
2026-04-02 16:48:10 -04:00
souldbminersmwc
234fb1655c hocmon: fix hocmon 2026-04-02 16:32:50 -04:00
souldbminersmwc
78a52df596 hocclk: fix gpu load 2026-04-01 20:11:06 -04:00
souldbminersmwc
8bffe2dfc1 sysclk: add SOCTHERM to UI 2026-04-01 16:39:11 -04:00
souldbminersmwc
064ecc6d25 i hate submodules i hate submodules i hate submodules 2026-04-01 16:13:16 -04:00
souldbminersmwc
2d5ba3a841 more submodule stuff 2026-04-01 16:09:49 -04:00
souldbminersmwc
3b2de89d2f fix submodules again 2026-04-01 16:08:42 -04:00
souldbminersmwc
b959ee864b re-add libultrahand submodule 2026-04-01 16:01:26 -04:00
souldbminersmwc
5f2d7a68a9 sysclk: remove sysclk old and libultrahand old 2026-04-01 16:00:54 -04:00
souldbminersmwc
554b66e25f Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-01 15:58:44 -04:00
souldbminersmwc
e20bafd6ab sysclk: remove old hocclk, bump version 2026-04-01 15:58:40 -04:00
Lightos1
dc2252a7c6 add pllx to sysclkFormatThermalSensor 2026-04-01 21:58:04 +02:00
Lightos1
80fa802e88 fix typo 2026-04-01 17:18:31 +02:00
Lightos1
944c1ef1f2 Always get pllx 2026-04-01 17:17:25 +02:00
Lightos1
da0806df5d Chinese translation by q1332348216-glitch 2026-04-01 16:00:33 +02:00
Lightos1
eda88210a9 remove commented out code 2026-04-01 15:40:01 +02:00
Lightos1
bb1ae1d816 Fix sleep 2026-04-01 15:38:45 +02:00
Lightos1
0a2a4cbd6b Revert "Update board.cpp"
This reverts commit 46d7b4e5fe.
2026-04-01 15:36:29 +02:00
Lightos1
2607034b7a Revert "Rename files"
This reverts commit 5da2ec11db.
2026-04-01 13:07:03 +02:00
Lightos1
5da2ec11db Rename files 2026-04-01 13:04:48 +02:00
souldbminersmwc
46d7b4e5fe Update board.cpp 2026-03-31 19:35:37 -04:00
souldbminersmwc
4358df0308 wip sleep fix 2026-03-31 19:34:54 -04:00
Lightos1
4103777f48 add gym goat to startup log 2026-03-31 23:40:35 +02:00
Lightos1
bf17e53c8f add soctherm 2026-03-31 23:26:18 +02:00
souldbminersmwc
8f6a5eee28 finish rewrite (for old version) 2026-03-31 16:50:16 -04:00
souldbminersmwc
6d0de115eb Revert "bump version"
This reverts commit 91c12b9128.
2026-03-29 14:45:13 -04:00
souldbminersmwc
1f2999df2f Revert "sysclk: add PWM dimming"
This reverts commit ec661ac1c0.
2026-03-29 14:45:09 -04:00
souldbminersmwc
1f2b3848e4 Revert "sysclk: refinements to pwm dimming"
This reverts commit ce99462081.
2026-03-29 14:45:07 -04:00
souldbminersmwc
955d009f54 Revert "sysclk: remove stray e"
This reverts commit 6607c287db.
2026-03-29 14:45:05 -04:00
souldbminersmwc
6607c287db sysclk: remove stray e 2026-03-28 18:53:39 -04:00
souldbminersmwc
ce99462081 sysclk: refinements to pwm dimming 2026-03-28 18:49:36 -04:00
souldbminersmwc
91c12b9128 bump version 2026-03-28 16:22:58 -04:00
souldbminersmwc
99e5cfc97e Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-03-28 16:20:44 -04:00
souldbminersmwc
ec661ac1c0 sysclk: add PWM dimming 2026-03-28 16:20:42 -04:00
Souldbminer
1de73c4b74 Merge pull request #55 from Angelblaster/patch-7
Update ko.json
2026-03-27 16:09:36 -04:00
Angelblaster
8a3b6f9775 Update ko.json
Before →After



   "Governor Settings": "주지사 설정", 

→

   "Governor Settings": "거버너 설정",

--------------------------------------------------------------------------------------------------

    "refresh rates may cause stress": "새로 고침 빈도는 스트레스를 유발할 수 있습니다",
    "or damage to your display! ": "또는 디스플레이가 손상되었습니다!",
    "Proceed at your own risk!": "자신의 책임하에 진행하십시오!",

→

   "refresh rates may cause stress": "디스플레이 주사율 빈도 변경은",
   "or damage to your display! ": " 기기에 손상이 발생될 수 있습니다!",
   "Proceed at your own risk!": "책임하에 주의해서 사용하십시오!",

--------------------------------------------------------------------------------------------------


    "RAM Timing Reductions": "RAM 타이밍 감소",

→

    "RAM Timing Reductions": "RAM 타이밍 편집기",

--------------------------------------------------------------------------------------------------

    "CPU UV": "CPU UV",

→

    "CPU UV": "CPU 언더볼트",

--------------------------------------------------------------------------------------------------

    "Extreme UV Table": "극자외선 테이블",

→

    "Extreme UV Table": "익스트림 테이블",

--------------------------------------------------------------------------------------------------

    "GPU Voltage Table": "GPU 전압 표",

→

    "GPU Voltage Table": "GPU 전압 테이블",

--------------------------------------------------------------------------------------------------

    "1075MHz without UV, 1152MHz on SLT": "UV 없이 1075MHz, SLT에서 1152MHz",
    "or 1228MHz on HiOPT can cause ": "또는 HiOPT에서 1228MHz를 사용하면",
    "permanent damage to your Switch!": "스위치가 영구적으로 손상되었습니다!",

→

    "1075MHz without UV, 1152MHz on SLT": "UV 없이 1075MHz, SLT에서 1152MHz",
    "or 1228MHz on HiOPT can cause ": "또는 HiOPT에서 1228MHz를 사용하면",
    "permanent damage to your Switch!": "스위치가 영구적으로 손상될 수 있습니다!",
2026-03-26 10:06:28 +09:00
Lightos1
7f0f743f47 remove old files 2026-03-25 18:09:22 +01:00
Lightos1
5a0ae8da5b Recover changes 2026-03-25 18:06:22 +01:00
Souldbminer
77fddda8a9 Merge pull request #54 from Angelblaster/patch-6
Update ko.json
2026-03-24 18:19:04 -04:00
souldbminersmwc
2828687a18 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-03-24 18:18:24 -04:00
souldbminersmwc
0c1f9e661c Delete dist.zip 2026-03-24 18:18:18 -04:00
Angelblaster
52aa25b212 Update ko.json
38)
"Boost Mode": "부스트 모드",  →     "Overwrite Boost Mode": "부스트 모드 덮어쓰기",

Other partial Korean corrections
2026-03-25 01:23:17 +09:00
Lightos1
3e98704a93 Merge pull request #52 from Angelblaster/patch-4
Update ko.json
2026-03-24 12:00:21 +01:00
Angelblaster
4a7277d8bd Update ko.json
I have corrected some of the Korean.
2026-03-24 19:57:55 +09:00
Lightos1
143de18f3a Merge pull request #51 from Angelblaster/patch-3
Update ko.json
2026-03-24 11:08:39 +01:00
Angelblaster
8626854d10 Update ko.json
I have corrected some of the Korean.
2026-03-24 19:06:23 +09:00
souldbminersmwc
6bd3ba7b3d final changes 2026-03-22 19:25:47 -04:00
souldbminersmwc
bc06d65b72 sysclk: update translations 2026-03-22 19:14:52 -04:00
souldbminersmwc
7045332ef2 sysclk: fix build script 2026-03-22 19:07:30 -04:00
souldbminersmwc
35bc76074c sysclk: add merged manual translations 2026-03-22 19:03:38 -04:00
souldbminersmwc
cdb725dc86 sysclk: update translation 2026-03-22 18:57:17 -04:00
souldbminersmwc
215ccf9001 sysclk: add machine translations for all languages supported by ultrahand 2026-03-22 18:51:23 -04:00
souldbminersmwc
ec21e47b53 sysclk: rework selection method and fix translation bugs 2026-03-22 18:22:38 -04:00
souldbminersmwc
17e27ad6e9 sysclk: fix default text in temporary overrides for display 2026-03-22 16:55:36 -04:00
souldbminersmwc
7489a861f6 sysclk: fix translation bug 2026-03-22 16:52:22 -04:00
souldbminersmwc
ef65b4dea9 Update all submodules to latest upstream commits 2026-03-22 15:41:10 -04:00
souldbminersmwc
0829cc8545 fix commit stuff 2026-03-22 15:19:19 -04:00
souldbminersmwc
5fb0899bb8 sysclk: fix label 2026-03-21 18:18:21 -04:00
souldbminersmwc
c17d5a3999 sysclk: add missing line, new binaries 2026-03-21 18:15:52 -04:00
souldbminersmwc
42045c5c88 sysclk: more minor changes 2026-03-21 17:52:56 -04:00
souldbminersmwc
6e6f7b4f9f sysclk: display max clock rework 2026-03-21 17:51:01 -04:00
souldbminersmwc
0496839500 sysclk: remove redundant profile options 2026-03-21 17:42:57 -04:00
souldbminersmwc
87911b8b9e sysclk: small TDP rework 2026-03-21 17:38:52 -04:00
souldbminersmwc
d3c441f8ca sysclk: change cpu bugfix wait duration
should fix on 2703mhz
2026-03-21 17:33:02 -04:00
souldbminersmwc
af6b347762 sysclk: fix iddq multiplication 2026-03-21 17:11:26 -04:00
souldbminersmwc
96b6459282 sysclk: read wafer cord 2026-03-21 17:09:10 -04:00
souldbminersmwc
371577954b Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-03-21 16:04:19 -04:00
souldbminersmwc
6bdb79fc15 sysclk: remove unused value 2026-03-21 16:04:13 -04:00
Lightos1
e625e4251a add sched off code to rewrite 2026-03-21 18:02:05 +01:00
Lightos1
5d7a77074b actually add uv tuning values... clean this code up 2026-03-21 17:44:01 +01:00
Lightos1
3b907d3ca0 Add live tuning adjustment to rewrite 2026-03-21 17:37:52 +01:00
Lightos1
a697cac43c Add message to assertion 2026-03-21 17:03:46 +01:00
Lightos1
3d5147a6e2 merge pcv hijacking into voltage code 2026-03-21 17:01:50 +01:00
Lightos1
f46d0a714b actually fix the typo 2026-03-21 16:56:10 +01:00
Lightos1
3896c89a5c Revert "Fix typo"
This reverts commit 9afd3d44bd, reversing
changes made to 95930746d2.
2026-03-21 16:54:07 +01:00
Lightos1
dd645bb352 fix typo.
:Merge branch 'main' of https://github.com/horizon-oc/Horizon-OC
2026-03-21 16:48:20 +01:00
Lightos1
9afd3d44bd Fix typo 2026-03-21 16:48:13 +01:00
Lightos1
5db6ac9f25 Lol, we tried pushing code at the same time
Merge branch 'main' of https://github.com/horizon-oc/Horizon-OC
2026-03-21 16:45:00 +01:00
Lightos1
95930746d2 Merge temp and power into sensor 2026-03-21 16:44:32 +01:00
souldbminersmwc
81e6fb9699 sysclk: fix vrr delay
this is unfixable unfortunately due to quirks
2026-03-21 11:44:17 -04:00
souldbminersmwc
8b9fbdf13d sysclk: fix governor min hz 2026-03-21 11:39:50 -04:00
souldbminersmwc
901ab93e58 sysclk: reorder governor trackbars 2026-03-21 11:28:00 -04:00
souldbminersmwc
eb75fb2274 sysclk: fix haschanged logic 2026-03-21 11:02:05 -04:00
souldbminersmwc
dab17f527e sysclk: fix another logic error 2026-03-21 10:59:52 -04:00
souldbminersmwc
aabff0e948 sysclk: fix VRR issues 2026-03-21 10:58:35 -04:00
souldbminersmwc
610bf9ad82 sysclk: fix logic errors 2026-03-21 10:51:03 -04:00
souldbminersmwc
1a42ad1a67 sysclk: RETRO super timings till 60hz, improve support 2026-03-20 20:57:41 -04:00
souldbminersmwc
71f31e8f6a fix check in RETRO SUPER detection code 2026-03-20 20:45:06 -04:00
souldbminersmwc
d6bf10d113 sysclk: retro super support 2026-03-20 20:43:27 -04:00
souldbminersmwc
decd02b564 change display max to 75hz
some good displays can hit 75hz
2026-03-20 20:05:51 -04:00
souldbminersmwc
3b681f8c90 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-03-20 20:03:38 -04:00
souldbminersmwc
95e68bd27e sysclk: fix refresh rate on lite
also increase heap size due to larger ipc packets
2026-03-20 20:03:36 -04:00
Lightos1
dfd5af7e80 add display func, todo: put into different file 2026-03-20 21:31:34 +01:00
Lightos1
0afef60198 add power 2026-03-20 21:24:55 +01:00
Lightos1
714eebb889 add temps 2026-03-20 21:22:02 +01:00
Lightos1
a5f58bfb3a add profile stuff 2026-03-20 21:16:49 +01:00
Lightos1
c29d6244a4 Fix typo 2026-03-20 21:09:43 +01:00
Lightos1
130f9101a2 Add dram id functions 2026-03-20 21:08:55 +01:00
Lightos1
308f39694c Add back misc thread 2026-03-20 21:02:24 +01:00
Lightos1
9e916b5d0f more formating 2026-03-20 20:47:35 +01:00
Lightos1
c2610472ce rewrite: formating 2026-03-20 20:45:59 +01:00
Lightos1
2dbd436307 rewrite: add freq functions 2026-03-20 20:43:08 +01:00
Lightos1
d525991beb Add info about power domains 2026-03-20 19:30:17 +01:00
Lightos1
b3ff426bfe GpuLoadThread: use proper types edition 2 2026-03-20 19:28:17 +01:00
Lightos1
958ca0530d GpuLoadThread: use proper types 2026-03-20 19:27:42 +01:00
Lightos1
981874a450 Change Result * to void * 2026-03-20 19:26:23 +01:00
Lightos1
4502dc6fdb Rewrite: full load 2026-03-20 19:24:45 +01:00
Lightos1
cc78b6193a Rename board_init.cpp -> board.cpp 2026-03-20 19:07:23 +01:00
Lightos1
327d64f22d Rewrite: Add real volts 2026-03-20 19:06:46 +01:00
Lightos1
c36052d477 remove new lines 2026-03-20 18:55:58 +01:00
Lightos1
bc9c30de56 rewrite: add Regulator type to ram oc dvfs 2026-03-20 18:54:48 +01:00
Lightos1
e1c3a7d018 Rewrite: add dvfs 2026-03-20 18:42:52 +01:00
Lightos1
179aee88af *actually* remove it for real 2026-03-20 18:09:42 +01:00
Lightos1
df4a59c269 fully uncomment miscThreadFunc 2026-03-20 18:05:56 +01:00
Lightos1
4d389cda63 what even is miscThreadFunc? 2026-03-20 18:05:00 +01:00
Lightos1
ae56a85811 Hoc-clk rewrite: More load implementation 2026-03-20 18:01:50 +01:00
Lightos1
3e75bd5b95 Start hoc-clk rewrite 2026-03-20 17:46:42 +01:00
souldbminersmwc
2454afd58f sysclk: fix resolution driver again 2026-03-19 19:57:03 -04:00
souldbminersmwc
7244093f21 sysclk: fix resolution 2026-03-19 19:50:20 -04:00
souldbminersmwc
52894e4c93 add resolution 2026-03-19 19:44:33 -04:00
souldbminersmwc
4e0b54c1a8 sysclk: refactor governor settings gui 2026-03-19 19:13:39 -04:00
souldbminersmwc
b5876ede0e sysclk: rework labels and warnings, bump version 2026-03-19 16:50:21 -04:00
souldbminersmwc
9558fd67bb update binaries 2026-03-17 20:33:19 -04:00
souldbminersmwc
59b62a835b misc: fix config bug 2026-03-17 20:30:33 -04:00
souldbminersmwc
ad6847ec59 Update zh-tw.json 2026-03-16 18:38:04 -04:00
Lightos1
061c255978 Change eristaCpuDvfsTable max to 2091, this is fine because vmax exists 2026-03-16 20:04:23 +01:00
Lightos1
bb44d0907d Fix dvfs yet again, todo make this not shit 2026-03-16 20:01:52 +01:00
Lightos1
2215b17a54 Merge pull request #45 from nangongjing1/main
Incorporate Traditional Chinese into Horizon-oc-overlay.
2026-03-16 16:18:46 +01:00
南宫镜
cc52eab3f6 Add files via upload 2026-03-16 23:13:15 +08:00
Lightos1
155a4ce50a Remove redundant else 2026-03-15 21:24:35 +01:00
Lightos1
17739377bd Fix Typo, add todo and ordering cleanup 2026-03-15 21:14:52 +01:00
Lightos1
782fae1624 minimum vmax = 800 2026-03-15 20:49:56 +01:00
Lightos1
59ca1f18d6 Add modified secmon_smc_handler.cpp for panic codes in loader 2026-03-15 20:44:25 +01:00
Lightos1
c6cda6eb30 Remove soctherm region unlock 2026-03-15 20:43:31 +01:00
Lightos1
9f3c5d8de6 Add debug panic codes 2026-03-15 20:35:45 +01:00
Souldbminer
e4c9ac6ee3 Update customize.cpp 2026-03-15 13:05:45 -04:00
souldbminersmwc
5d6500523b bump version 2026-03-15 12:36:32 -04:00
souldbminersmwc
b111ccabb5 sysclk: fix cpugovernorminimumfreq not setting correctly 2026-03-15 12:22:30 -04:00
souldbminersmwc
b90a67aa43 update binaries 2026-03-15 12:21:59 -04:00
souldbminersmwc
9ed72839f4 sysclk: the code should compile now 2026-03-15 10:29:49 -04:00
souldbminersmwc
b8f50da45f more lite changes 2026-03-15 10:27:30 -04:00
souldbminersmwc
1e4dada672 remove display refresh rate on hoag 2026-03-15 10:20:46 -04:00
souldbminersmwc
a943d14807 Revert "sysclk: attempt to fix lite issues"
This reverts commit 2b3889c897.
2026-03-15 10:13:24 -04:00
Lightos1
2cf437ff34 Fix typo 2026-03-15 14:57:09 +01:00
Lightos1
7cbccbbb5b Simplify dram module switch statement 2026-03-15 14:07:12 +01:00
Lightos1
f79af5b6f7 Show ram module in about 2026-03-15 13:59:55 +01:00
Lightos1
05af1d04ff Don't use ram frequency editor for mariko 2026-03-15 13:09:11 +01:00
Lightos1
2753646f06 Revert "Correct version"
This reverts commit 7ec9827db5.
2026-03-14 13:07:47 +01:00
souldbminersmwc
2b3889c897 sysclk: attempt to fix lite issues 2026-03-13 19:55:13 -04:00
souldbminersmwc
e01e346dea sysclk: refine menu 2026-03-13 19:18:26 -04:00
souldbminersmwc
7735037ad9 sysclk: cpu governor is no longer experimental 2026-03-13 18:49:03 -04:00
souldbminersmwc
b2bcd5fc3a sysclk: removed unused vrr config option 2026-03-13 18:40:42 -04:00
souldbminersmwc
a40ea357db sysclk: add per-game VRR setting 2026-03-13 18:35:38 -04:00
souldbminersmwc
7434c22772 sysclk: fix dvfs issue 2026-03-13 15:41:18 -04:00
Lightos1
f0eb25b88c Move sysmodule settings into its own submenu 2026-03-13 20:21:06 +01:00
Lightos1
7ec9827db5 Correct version 2026-03-13 19:48:02 +01:00
Lightos1
eeca31c92e 'merge' frame padding fix 2026-03-13 19:46:47 +01:00
Lightos1
2cd736035c configurator: when uv is active, cpu vmin can only be adjusted in 25mv steps 2026-03-13 17:19:27 +01:00
Lightos1
bc99616e43 erista uv: minor technicality fix 2026-03-13 16:44:11 +01:00
Lightos1
732f27fcf6 add back assertion 2026-03-13 16:25:59 +01:00
Lightos1
54e8465e47 Fix erista mrf 2026-03-13 16:22:47 +01:00
souldbminersmwc
7ceb02c001 bump version 2026-03-12 21:03:09 -04:00
souldbminersmwc
2450a348f1 sysclk: fix polling interval option 2026-03-12 20:57:58 -04:00
Souldbminer
7bd469939e Merge pull request #42 from Horizon-OC/saltynx
Saltynx integration + VRR
2026-03-12 20:53:00 -04:00
souldbminersmwc
89180359c0 todo: fix applet stuff 2026-03-12 20:52:38 -04:00
souldbminersmwc
4259ace5c4 sysclk: properly init applet service 2026-03-12 20:50:02 -04:00
souldbminersmwc
d7e5c38a62 sysclk: add VRR 2026-03-12 20:47:11 -04:00
souldbminersmwc
b601105998 sysclk: fix integration with saltynx 2026-03-12 18:39:57 -04:00
souldbminersmwc
ca07d0716f sysclk: fix up saltynx integration 2026-03-12 16:48:22 -04:00
souldbminersmwc
eb16df3450 fix pointers 2026-03-12 15:51:27 -04:00
souldbminersmwc
b44684760d fix defines 2026-03-12 15:51:06 -04:00
souldbminersmwc
be3fc1bb84 saltynx integration 2026-03-12 15:49:58 -04:00
souldbminersmwc
e781e67b43 sysclk: rework governor
now should work in more games and behave more like a schedutil (slow rampdown, fast rampup)

fixes issues in Kirby and the Forgotten Land
2026-03-11 19:03:46 -04:00
souldbminersmwc
40800ffe4b Revert "sysclk: add notification led driver"
This reverts commit 06e5d5e3d1.
2026-03-11 16:06:58 -04:00
souldbminersmwc
4e704e59e8 fix display oc 2026-03-11 16:06:55 -04:00
souldbminersmwc
06e5d5e3d1 sysclk: add notification led driver 2026-03-10 20:39:09 -04:00
souldbminersmwc
06010d7cd6 hocclk: fix 2397 boost clock 2026-03-10 18:12:28 -04:00
Souldbminer
2549cd9a71 Update README.md 2026-03-09 16:20:10 -04:00
souldbminersmwc
e89e35436e replace banner 2026-03-09 16:19:39 -04:00
Souldbminer
f5029ee3e9 actually remove live cpu uv enforced on erista 2026-03-09 15:44:39 -04:00
Lightos1
08a84e0a8b Clkmgr: remove forced live uv for erista 2026-03-09 20:39:20 +01:00
Lightos1
36c819de04 add cpuVoltDvfsPattern size assertion 2026-03-09 20:31:18 +01:00
Lightos1
f62987af4b remove cpuVoltDvfsOffsets 2026-03-09 20:28:59 +01:00
Lightos1
8d9b44d6ec Fix erista cpu uv, vmin, vmax 2026-03-09 20:26:23 +01:00
Lightos1
a5babb722d erista patches stuff 2026-03-09 20:25:40 +01:00
Lightos1
bde65f094d Misc3: Add missing ram load 2026-03-08 13:27:36 +01:00
Lightos1
3fa2cfabd3 Reorder menus/put settings into submenus to improve scrolling 2026-03-07 23:19:50 +01:00
souldbminersmwc
b8f4d02e4f sysclk: UI rework + bump version 2026-03-07 11:24:21 -05:00
souldbminersmwc
436b7feb8e sysclk: fix dvfs after set logic 2026-03-07 10:35:40 -05:00
souldbminersmwc
8bab4dd6ee Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-03-07 10:30:50 -05:00
souldbminersmwc
99dfea631b add rgltr services 2026-03-07 10:30:47 -05:00
Lightos1
16501bd6a8 Remove deprecated configurator 2026-03-01 21:37:31 +01:00
Lightos1
88201b7308 Add/update comments 2026-03-01 21:35:07 +01:00
Lightos1
2fb680b06a Remove ancient todo 2026-03-01 21:32:54 +01:00
Lightos1
fb8116107b correct CpuVoltOfficial for informational purposes 2026-03-01 21:30:54 +01:00
Lightos1
e6b83a1db5 Adjust l4t limit 2026-03-01 21:29:03 +01:00
Lightos1
607a19048b Extend dvfs offset to -80 2026-03-01 21:05:41 +01:00
Lightos1
0db831e0c3 lower cpu vmax 2026-03-01 16:57:38 +01:00
Lightos1
863cca507d lower cpu vmax validators 2026-03-01 16:54:19 +01:00
Lightos1
3916a252d4 Lower max max voltage 2026-03-01 16:53:46 +01:00
souldbminersmwc
1594b76851 100th commit 2026-02-26 15:34:45 -05:00
souldbminersmwc
9e14fc5241 add some more docs 2026-02-24 18:32:54 -05:00
souldbminersmwc
54fd3e2fd1 Update registers.h 2026-02-24 18:27:12 -05:00
souldbminersmwc
2348f8dba2 apparently, claude can't generate defines correctly 2026-02-24 18:24:05 -05:00
souldbminersmwc
f0a3dc48f9 fix live uv 2026-02-24 17:19:58 -05:00
souldbminersmwc
58ad43213b sysclk: add RAM table editor 2026-02-24 17:09:24 -05:00
souldbminersmwc
4a505b7238 update binaries 2026-02-24 16:25:28 -05:00
souldbminersmwc
7c6c0e68d2 Update pcv.cpp 2026-02-24 16:23:42 -05:00
souldbminersmwc
1c5b22710d Update pcv.cpp 2026-02-24 16:22:48 -05:00
souldbminersmwc
75755528b1 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-02-24 16:22:03 -05:00
Lightos1
4dadcaf574 Revert to defaults 2026-02-24 20:17:53 +01:00
Lightos1
5a88d53443 Erista uv stuff: this makes no sense 2026-02-24 20:16:10 +01:00
Lightos1
2bd5faa7d9 Erista uv & vmin fixes 2026-02-24 19:20:41 +01:00
Lightos1
29cb868f50 Improve formating 2026-02-24 09:08:09 +01:00
souldbminersmwc
4eb222f5aa ldr/sysclk: add 2397 erista cpu, fix cpu uv, add erista cpu max clock 2026-02-23 19:09:59 -05:00
souldbminersmwc
eca6ab2297 sysclk: fix erista cpu uv 2026-02-23 18:45:39 -05:00
souldbminersmwc
749e5147df ldr: add commented pll limit patch 2026-02-23 18:44:10 -05:00
souldbminersmwc
55e84d0051 sysclk: fix compile error 2026-02-23 15:08:06 -05:00
souldbminersmwc
8cd9727429 sysclk: fix clock capping bypass bug 2026-02-23 10:39:29 -05:00
Lightos1
660e839bed Extend emc unlock 2026-02-21 19:36:52 +01:00
Lightos1
afb16d2045 Remove lazy rl_dbi stuff 2026-02-21 15:34:38 +01:00
souldbminersmwc
f4b025f33c sysclk: remove display option on hoag and mark cpu governor as experemental 2026-02-20 19:27:04 -05:00
Souldbminer
e03c3b7be0 Update README.md 2026-02-20 17:04:40 -05:00
Lightos1
5a2ba5f785 Board: Fix no sched override 2026-02-20 19:12:51 +01:00
Lightos1
e1463dca05 Bump version 2026-02-20 16:55:34 +01:00
Lightos1
82972127a1 Boost mode: Cpu voltage bug workaround 2026-02-20 16:53:30 +01:00
Lightos1
272eaed351 UI reordering and text removal for smoother scrolling 2026-02-20 16:30:55 +01:00
Lightos1
989e67daac Overlay: Remove unsupported cpu max freq 2026-02-20 15:40:30 +01:00
Lightos1
dcec618ae9 Fix freqs not being set 2026-02-20 15:15:21 +01:00
Lightos1
bc12388b6d formating 2026-02-20 07:34:17 +01:00
Lightos1
6625488180 Formating 2026-02-20 07:33:51 +01:00
Lightos1
1209337af6 Erista: MRf and timing fixes 2026-02-20 07:32:10 +01:00
Souldbminer
0f608b1702 Revise GPU overclocking notes in README
Updated GPU overclocking notes for Erista and Mariko.
2026-02-19 19:59:57 -05:00
Souldbminer
cf547d517b Update testing contributors in README.md 2026-02-19 19:57:54 -05:00
Souldbminer
8c75c68dca Update LICENSE 2026-02-19 19:56:22 -05:00
souldbminersmwc
ab020c0a90 sysclk: code cleanup 2026-02-19 19:41:17 -05:00
souldbminersmwc
44dc402b54 sysclk: revise governor and change override logic 2026-02-19 18:54:21 -05:00
souldbminersmwc
a1d047f48d sysclk: remove atmosphere-libs 2026-02-19 16:36:20 -05:00
souldbminersmwc
cbed5e11ab sysclk: remove unnessesary code 2026-02-19 16:35:12 -05:00
souldbminersmwc
1ad3f6c441 sysclk: add GPU sched ini method 2026-02-19 16:33:55 -05:00
souldbminersmwc
820a26ba2a sysclk: add atmosphere-libs as submodule 2026-02-19 16:11:21 -05:00
souldbminersmwc
f0952119b6 sysclk: add CPU governor and more governor settings 2026-02-18 19:32:33 -05:00
souldbminersmwc
ca5ddbd779 sysclk: fix translation 2026-02-17 10:59:49 -05:00
souldbminersmwc
be49a27118 Update clock_manager.cpp 2026-02-16 18:41:59 -05:00
souldbminersmwc
190353dc11 sysclk: add on-the-fly CPU undervolt 2026-02-16 18:27:26 -05:00
souldbminersmwc
4a1772df77 sysclk: remove unused variable 2026-02-16 14:06:54 -05:00
souldbminersmwc
460d1b8471 sysclk: fix up refresh rate driver 2026-02-16 14:06:35 -05:00
souldbminersmwc
2493c798bc Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-02-15 17:44:48 -05:00
souldbminersmwc
7525baf567 sysclk: remove reversenx mode, some code cleanup, display upto 240hz, experemental settings config option, remove max display clock , bump version 2026-02-15 17:43:41 -05:00
Lightos1
3f9a5f61fb Add mrf for erista 2026-02-15 15:41:52 +01:00
souldbminersmwc
b9156d6861 sysclk: add sys-dock intergration 2026-02-14 21:16:14 -05:00
souldbminersmwc
5d59be7b77 sysclk: add experemental gpu sched override option 2026-02-14 20:55:44 -05:00
souldbminersmwc
dd4c5a8732 sysclk: make memmem proper 2026-02-14 20:27:43 -05:00
souldbminersmwc
837543fb0f Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-02-14 20:23:25 -05:00
souldbminersmwc
03ede8f171 sysclk: fix crashing on boot and raise minimum refresh rate on aula 2026-02-14 20:23:22 -05:00
Lightos1
078d8164fc Remove include 2026-02-15 01:29:11 +01:00
Lightos1
ceff8a083e Remove function in header 2026-02-15 01:28:49 +01:00
Lightos1
b32efcc177 Erista: dvb + more precise timings/timings from eos & formating 2026-02-15 01:27:50 +01:00
Lightos1
d57fccc463 Add cpu load, thanks masa for the help! 2026-02-14 21:47:25 +01:00
Souldbminer
26cf028f2d Update contact information for reporting vulnerabilities 2026-02-13 19:59:36 -05:00
souldbminersmwc
6e80c9a75f chore: depricate exosphere patch 2026-02-13 19:53:13 -05:00
souldbminersmwc
42bdfb55f2 remove charge current from overlay 2026-02-13 19:35:40 -05:00
souldbminersmwc
e0967a9fd6 sysclk: fix ui issues 2026-02-13 19:33:42 -05:00
souldbminersmwc
2406ce4f86 sysclk: fix real freq bug 2026-02-13 19:12:01 -05:00
souldbminersmwc
3b40a4a3e5 sysclk: turn on size compiler optimization 2026-02-13 16:36:04 -05:00
souldbminersmwc
38c408dde6 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-02-13 16:33:31 -05:00
souldbminersmwc
5e62eb3f5d sysclk: fix compile issue and remove 1152 no uv gpu 2026-02-13 16:33:29 -05:00
Lightos1
b4b5599ed2 sleep of 300'000 is enough 2026-02-13 12:15:46 +01:00
souldbminersmwc
b4917f3e1a sysclk: remove enforce board limit
this setting is bugged, so remove it
2026-02-12 19:55:15 -05:00
souldbminersmwc
e6b4cb6612 sysclk: add lineon's cpu volt bugfix
thanks, lineon!
2026-02-12 19:49:52 -05:00
souldbminersmwc
b1ca62ce61 sysclk: fix voltage display issue 2026-02-12 19:19:53 -05:00
souldbminersmwc
243f614887 Update clock_manager.cpp 2026-02-12 18:22:43 -05:00
souldbminersmwc
a8577378f6 Update misc_gui.cpp 2026-02-12 17:01:33 -05:00
souldbminersmwc
564703b7c5 sysclk: improve cpu volt bugfix 2026-02-12 17:00:31 -05:00
souldbminersmwc
5ef56bed25 sysclk: remove live timing update and fix profile change dvfs bug 2026-02-12 16:15:25 -05:00
Lightos1
afddb963a9 Remove dvfsOffset capping 2026-02-12 21:22:53 +01:00
Lightos1
aa72176196 Remove budget dvfs 2026-02-12 17:15:26 +01:00
Lightos1
7af0721847 DVFS: Safety check fix 2026-02-12 17:02:52 +01:00
Lightos1
2a6320e646 Bracket fix 2026-02-12 16:49:12 +01:00
Lightos1
fc212bb419 Bump version 2026-02-12 16:46:49 +01:00
Lightos1
14e0053335 DVFS: Add offset 2026-02-12 16:44:04 +01:00
Lightos1
8f28daceef DVFS: Add safety check 2026-02-12 16:12:36 +01:00
souldbminersmwc
f1044673d0 sysclk: modify dvfs logic 2026-02-11 19:28:43 -05:00
souldbminersmwc
61a0ebffee sysclk: fully fix dvfs 2026-02-11 19:22:25 -05:00
souldbminersmwc
8de8f4013e sysclk: fix dvfs config option 2026-02-11 19:11:02 -05:00
souldbminersmwc
dd447553d4 sysclk: make dvfs work 2026-02-11 19:09:55 -05:00
Lightos1
11c456e00c Fix random } 2026-02-11 17:42:10 +01:00
Lightos1
5efb4bdd6b Add hijack dvfs from sys-clk-eos 2026-02-11 16:57:26 +01:00
Lightos1
e331f1249b Why did this not commit? 2026-02-11 11:08:29 +01:00
Lightos1
786467d7ea add r2w fine tuning 2026-02-11 11:05:45 +01:00
Lightos1
496e77301b bump version 2026-02-11 01:06:52 +01:00
Lightos1
d657f1d156 Fix typo 2 2026-02-10 15:33:47 +01:00
Lightos1
7ab5c2f540 Fix typo 2026-02-10 15:33:03 +01:00
Lightos1
71900721cf Make AUTO_RAM default (for now) 2026-02-10 15:26:12 +01:00
Lightos1
0bc9547701 Add advanced header 2026-02-10 15:12:17 +01:00
Lightos1
96ac254022 Add w2r fine tune 2026-02-10 15:04:28 +01:00
Souldbminer
92f378a80f fix svfs again 2026-02-09 18:59:09 -05:00
Lightos1
589af01ad8 Budget dvfs fix 2026-02-10 00:39:22 +01:00
Lightos1
e7f14b88e5 Replace floor with ceil 2026-02-09 16:29:05 +01:00
souldbminersmwc
35a269e4a7 Update README.md 2026-02-08 15:51:46 -05:00
souldbminersmwc
ff95ca527f Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-02-08 15:33:53 -05:00
souldbminersmwc
550e4e5204 Delete HOC_AUTO_RAM_TEST.zip 2026-02-08 15:33:39 -05:00
Lightos1
a088c4795a Update readme 2026-02-08 18:52:23 +01:00
Lightos1
e8a264fa11 New timings, fix budget gpu dvfs 2026-02-08 18:46:10 +01:00
souldbminersmwc
de681c20a0 ldr: code cleanup 2026-02-08 12:00:27 -05:00
souldbminersmwc
22aef264ab Update pcv_mariko.cpp 2026-02-08 11:57:54 -05:00
souldbminersmwc
309717c468 ldr: fix auto_ram vmin 2026-02-08 11:57:43 -05:00
souldbminersmwc
9b91d7487c ldr: rename namespace 2026-02-08 11:56:04 -05:00
souldbminersmwc
c0b458243c ldr: add auto ram vmin 2026-02-08 11:54:21 -05:00
souldbminersmwc
7d9c60cdfe Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-02-07 19:20:33 -05:00
souldbminersmwc
12c9effec2 Update README.md 2026-02-07 19:20:31 -05:00
Souldbminer
6eeab63075 Rearrange CPU clock entries in README
Reordered CPU clock entries for clarity.
2026-02-07 18:53:41 -05:00
Souldbminer
2d81c5e127 Merge pull request #34 from OEMunlag/develop
Remove dist.zip hash on actions
2026-02-07 18:51:12 -05:00
souldbminersmwc
b1739cea74 sysclk: rework clock capping 2026-02-07 18:38:37 -05:00
souldbminersmwc
aa607ab1a7 sysclk: safety display changes 2026-02-07 18:28:49 -05:00
souldbminersmwc
d1242a17d4 sysclk: 2133MHz is a JEDEC clock 2026-02-07 18:23:49 -05:00
2fort sink
80701c2891 Merge branch 'Horizon-OC:develop' into develop 2026-02-07 20:18:15 -03:00
souldbminersmwc
cfd0d84ac8 update dist 2026-02-07 18:10:21 -05:00
souldbminersmwc
c33aabdc84 chore: code cleanup 2026-02-07 18:10:12 -05:00
2fort sink
c70fd82e60 Modify build.yml to set commit SHA and dist
Updated the GitHub Actions workflow to include a distribution file with the commit SHA.
2026-02-07 20:07:56 -03:00
2fort sink
574c248d68 Merge branch 'Horizon-OC:develop' into develop 2026-02-07 19:55:21 -03:00
souldbminersmwc
ebcf07dab8 update files 2026-02-07 17:51:38 -05:00
souldbminersmwc
d170ca92ad ldr: rework reserved entries 2026-02-07 17:51:18 -05:00
souldbminersmwc
cab8a14b99 Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-02-06 19:43:48 -05:00
souldbminersmwc
b623dabce5 sysclk: fix 1963mhz bug 2026-02-06 19:42:58 -05:00
Dominator
e5eb49f2e5 Merge pull request #33 from Blaise-25/develop 2026-02-06 01:47:22 +02:00
Blaise-25
9f0a609735 Update README.md 2026-02-05 15:20:12 -08:00
Souldbminer
19a2e562bc Update README.md 2026-02-05 16:12:30 -05:00
souldbminersmwc
73c3311251 Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-02-04 19:33:30 -05:00
souldbminersmwc
eb7fca1c1a erista ram overclock support + reserved entried
if you use hoc-clk 0.33 and want to update follow these steps

go to config/horizon-oc/config.ini
remove these two entries:

crc32=xxxxxxxxxxx
is_first_load=0
2026-02-04 19:33:10 -05:00
Dominator
ad1f070a6b Update README.md 2026-02-05 01:17:10 +02:00
2fort sink
0ae65c8740 Merge branch 'Horizon-OC:develop' into develop 2026-02-04 18:07:27 -03:00
2fort sink
39fbe25afc Update build.yml 2026-02-04 18:07:16 -03:00
souldbminersmwc
3bad144280 sysclk: add cat script 2026-02-03 19:01:49 -05:00
souldbminersmwc
123d4b6080 Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-02-03 18:57:02 -05:00
souldbminersmwc
2f6ed69b92 sysclk: refine misc gui 2026-02-03 18:56:48 -05:00
Souldbminer
337f7e6ac0 Merge pull request #32 from OEMunlag/develop
proper github actions build
2026-02-03 15:57:29 -05:00
2fort sink
a5efa7ae72 Add files via upload 2026-02-03 16:55:48 -03:00
2fort sink
a8ee389681 Revert to last working
Updated the Docker image for the build container and removed unnecessary steps for creating compiler shims. Added packaging of the dist folder as a ZIP file with the commit SHA.
2026-02-03 16:40:38 -03:00
2fort sink
54554bacea Change Docker image and install switch-dev
Updated the Docker image for the build container and added a step to install switch-dev.
2026-02-03 16:28:09 -03:00
2fort sink
3da2fc4b6d Merge branch 'Horizon-OC:develop' into develop 2026-02-03 15:05:11 -03:00
2fort sink
83287b1f5c Update build.yml 2026-02-02 21:53:24 -03:00
2fort sink
4f6756c609 Update build.yml 2026-02-02 21:29:05 -03:00
souldbminersmwc
ef2434c457 try to fix status monitor issues 2026-02-02 19:13:08 -05:00
2fort sink
b0237a2042 Update build.yml 2026-02-02 21:07:54 -03:00
2fort sink
c6275eb977 Update build.yml 2026-02-02 20:51:49 -03:00
2fort sink
72961aa2f1 Update build.yml 2026-02-02 20:46:58 -03:00
2fort sink
9c60697344 Merge branch 'Horizon-OC:develop' into develop 2026-02-02 20:45:15 -03:00
2fort sink
f786d97de1 Update build.yml 2026-02-02 20:44:06 -03:00
2fort sink
20389043ac Enhance build.yml with ccache shims for compilers
very hacky I know but we also fix exosphere hopefully with this
2026-02-02 20:37:56 -03:00
souldbminersmwc
00b9d8550e ldr: fix erista cpu validator
this DOES NOT fix erista cpu uv, but is probably part of the issue
2026-02-02 18:18:22 -05:00
2fort sink
0c0a4b3d6b Merge branch 'Horizon-OC:develop' into develop 2026-02-01 19:14:07 -03:00
2fort sink
61fd9d74d3 Update build.yml 2026-02-01 19:14:00 -03:00
2fort sink
f36383d0e7 Update build.yml 2026-02-01 18:58:52 -03:00
souldbminersmwc
606cafbaad sysclk: rework TDP 2026-02-01 16:33:15 -05:00
2fort sink
cff4267a68 Fix ROOT variable assignment in build workflow 2026-02-01 18:32:01 -03:00
2fort sink
65e7d83972 Update build.yml 2026-02-01 18:26:30 -03:00
2fort sink
e2533e2bb0 Patch exosphere and build, fix comment and zip making redundancy 2026-02-01 18:19:23 -03:00
souldbminersmwc
0d4ca24538 sysclk: add kip auto update feature 2026-02-01 16:15:08 -05:00
2fort sink
6c94f5754c Update build.yml 2026-02-01 17:58:52 -03:00
2fort sink
aaf77997e1 Update build.yml 2026-02-01 17:52:44 -03:00
2fort sink
c933e02aed Merge branch 'Horizon-OC:develop' into develop 2026-02-01 17:46:47 -03:00
2fort sink
770c25dbcb build hoc-clk and shallow clone ams
Updated the build process to use shallow clones and added a new build step for the hoc-clk sysmodule and overlay.
2026-02-01 17:44:20 -03:00
souldbminersmwc
bf8a6c4785 sysclk: fix uv3 cappings 2026-02-01 15:27:17 -05:00
2fort sink
dbb4d698a0 Increase make parallel jobs and update file paths 2026-02-01 16:12:26 -03:00
2fort sink
6ae084433f Update build.yml 2026-02-01 15:58:29 -03:00
2fort sink
32f19c4f0e Update build.yml 2026-02-01 15:48:47 -03:00
2fort sink
05cf2ab4dd Clone Atmosphere with specific branch from file 2026-02-01 14:28:37 -03:00
2fort sink
13a4ea4ca0 create ams_ver.txt
Should be self-explanatory, we use this for the actions build system
2026-02-01 14:27:02 -03:00
2fort sink
e2ef52e095 Update build.yml 2026-02-01 14:25:15 -03:00
2fort sink
bbd3b1a0fc Refactor build.yml for environment setup and ccache
Updated the build workflow to set environment variables using GitHub Actions environment files and improved the ccache installation step.
2026-02-01 14:22:31 -03:00
2fort sink
8dcfb55646 retry
cache and actions cache
2026-02-01 14:15:59 -03:00
2fort sink
ae0f4c7963 Delete .github/workflows/build.yml 2026-02-01 13:54:21 -03:00
2fort sink
222db76200 refactor this test testing woo waa waewewe waowowoewewahaaswadisadasud
Updated comments and modified build steps in the GitHub Actions workflow.
2026-02-01 13:52:55 -03:00
souldbminersmwc
2376b85d3a remove build action
way too slow to be practical
2026-01-31 18:52:23 -05:00
souldbminersmwc
26fb76e448 Update build.yml 2026-01-31 18:42:54 -05:00
souldbminersmwc
159c7567c0 Update build.yml 2026-01-31 18:41:42 -05:00
souldbminersmwc
39366ce238 Update build.yml 2026-01-31 18:40:33 -05:00
souldbminersmwc
da631ad268 Create build.yml 2026-01-31 18:37:47 -05:00
souldbminersmwc
d508177d4b Delete build.yml 2026-01-31 17:31:47 -05:00
souldbminersmwc
d8527410de Update build.yml 2026-01-31 17:30:17 -05:00
souldbminersmwc
bae7216de0 Update build.yml 2026-01-31 17:29:34 -05:00
souldbminersmwc
b3ddf172e8 Update build.yml 2026-01-31 17:28:25 -05:00
souldbminersmwc
cdaea8f729 fix build script 2026-01-31 17:27:18 -05:00
souldbminersmwc
918ca5757c chore: add build script 2026-01-31 17:25:30 -05:00
souldbminersmwc
93cb0e99c4 build: remove broken build script 2026-01-31 17:20:06 -05:00
souldbminersmwc
4692529346 chore: clean up repo 2026-01-31 17:19:33 -05:00
souldbminersmwc
8de1191ed5 sysclk: rename ipc service 2026-01-31 17:15:06 -05:00
souldbminersmwc
1c21187f73 sysclk: remove unused stuff 2026-01-31 17:12:50 -05:00
souldbminersmwc
2646995b72 sysclk: rework clock capping 2026-01-30 19:44:12 -05:00
souldbminersmwc
cf21342b2e Update pcv_erista.cpp 2026-01-28 15:45:45 -05:00
souldbminersmwc
3709fb3fba ldr: fix default gpu vmin 2026-01-28 15:41:50 -05:00
souldbminersmwc
ea754707ee sysclk: fix erista gpu vmin 2026-01-28 15:39:56 -05:00
souldbminersmwc
bb96267d6c Adjust license 2026-01-26 19:30:18 -05:00
souldbminersmwc
a1fa16da3c misc changes 2026-01-26 19:29:45 -05:00
souldbminersmwc
aad126d611 rename uv table 2026-01-26 19:26:30 -05:00
souldbminersmwc
835189febc update 2026-01-26 19:23:28 -05:00
souldbminersmwc
a9cf59fe28 ldr: UV3 doesn't exist anymore 2026-01-26 19:08:27 -05:00
souldbminersmwc
7708e4bae6 Update pcv.cpp 2026-01-26 19:07:52 -05:00
souldbminersmwc
8c71227c63 sysclk: improve cpu fix 2026-01-26 16:41:35 -05:00
souldbminersmwc
627732326b Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-01-26 16:26:09 -05:00
souldbminersmwc
f5d9761853 fix cpu bug 2026-01-26 16:25:25 -05:00
Lightos1
1d219e9425 Extend vdd2 validators 2026-01-25 13:07:13 +01:00
Lightos1
2c14ddad5c Extend vddq validator 2026-01-25 11:41:05 +01:00
souldbminersmwc
fee7e7a4f4 Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-01-24 18:54:58 -05:00
souldbminersmwc
caeee5920b sysclk: fix cpu boost clock 2026-01-24 18:54:56 -05:00
Lightos1
71fbeef32a Revert "Update ptm.cpp", worked before
This reverts commit 86fce20b27.
2026-01-24 22:14:19 +01:00
souldbminersmwc
86fce20b27 Update ptm.cpp 2026-01-24 15:17:50 -05:00
Lightos1
5012836c7e finally fix voltage bug for real 2026-01-24 20:43:35 +01:00
Lightos1
af1c36d00a Fix 1333 base latency at >=2533Mhz 2026-01-24 20:13:48 +01:00
souldbminersmwc
5dca0a9d9b Update clock_manager.cpp 2026-01-23 19:49:43 -05:00
souldbminersmwc
d3b1e9d31d sysclk: revise cpu volt bugfix 2026-01-23 18:33:09 -05:00
souldbminersmwc
64301c6d33 sysclk: fix warnings 2026-01-21 19:55:25 -05:00
souldbminersmwc
9b4544856e Update format.h 2026-01-21 18:54:23 -05:00
souldbminersmwc
d8104bfc6f fix submodules yet again 2026-01-21 18:32:20 -05:00
souldbminersmwc
39e666599d finish submodule linking 2026-01-21 18:30:32 -05:00
souldbminersmwc
d3ff7857e7 sysclk: add licensed and link libultrahand as submodules 2026-01-21 18:29:59 -05:00
souldbminersmwc
662473b2c0 sysclk: include libnx globally 2026-01-21 18:23:30 -05:00
souldbminersmwc
051c637dd8 sysclk: add safety check to misc gui 2026-01-21 18:19:30 -05:00
souldbminersmwc
28a772c546 sysclk: add danger/warning colors to cpu max clock 2026-01-21 18:16:59 -05:00
souldbminersmwc
4a06176897 sysclk: add display unsafe freq option 2026-01-21 18:15:28 -05:00
souldbminersmwc
7325905476 Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-01-21 16:47:25 -05:00
souldbminersmwc
ed8eed2c57 bump version 2026-01-21 16:47:20 -05:00
Lightos1
9723fdc2e0 Lazy timing fix, clean up later 2026-01-21 22:45:47 +01:00
souldbminersmwc
2856f6f778 sysclk: add AUTO vmin to overlay and add warning/danger for LCD 2026-01-21 16:44:24 -05:00
Lightos1
4187daf029 Revert "pcv_mariko: unhardcode pllmb divisor"
This reverts commit 12e6f114a8.
2026-01-21 22:32:54 +01:00
souldbminersmwc
c479f9c53c ldr: allow 1963 cpu cap + revise gpu UV 2026-01-21 16:27:14 -05:00
souldbminersmwc
a5357e298a sysclk: fix typo 2026-01-21 15:53:33 -05:00
souldbminersmwc
045bd81887 pcv: fix auto vmin on low speedos 2026-01-21 15:52:54 -05:00
souldbminersmwc
ae22e70602 Merge branch 'develop' of https://github.com/Horizon-OC/Horizon-OC into develop 2026-01-21 15:49:50 -05:00
souldbminersmwc
12e6f114a8 pcv_mariko: unhardcode pllmb divisor 2026-01-21 15:49:43 -05:00
Lightos1
d6fec8bdaa Fix r2p 2026-01-21 21:49:07 +01:00
souldbminersmwc
5a8a9ae79d ldr: fix wev 2026-01-21 15:48:17 -05:00
souldbminersmwc
6d0399f352 ldr/sysclk: update tWRL setting method 2026-01-21 15:45:51 -05:00
Lightos1
382f6d5834 Bump version 2026-01-21 19:53:30 +01:00
Lightos1
679fd4fd79 1866/1600 support 2026-01-21 18:50:36 +01:00
souldbminersmwc
c4a59d72b7 Update horizon-oc-overlay.ovl 2026-01-18 19:39:20 -05:00
souldbminersmwc
cc2f58f1d7 fix settings button 2026-01-18 19:38:22 -05:00
385 changed files with 31228 additions and 55284 deletions

176
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,176 @@
name: Build Horizon OC Zeus
on:
push:
branches: [ develop, main, master ]
pull_request:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
# Minimal devkitA64 container
container:
image: devkitpro/devkita64:20251231
steps:
- name: Checkout repository and submodules # needed for hoc-clk
uses: actions/checkout@v6.0.2
with:
submodules: recursive
# -------------------------------------------------
# Fix PATH for devkitA64 and devkitARM
# -------------------------------------------------
- name: Set devkitPro PATH
run: |
echo "DEVKITPRO=/opt/devkitpro" >> $GITHUB_ENV
echo "DEVKITA64=/opt/devkitpro/devkitA64" >> $GITHUB_ENV
echo "DEVKITARM=/opt/devkitpro/devkitARM" >> $GITHUB_ENV
echo "PATH=/opt/devkitpro/devkitA64/bin:/opt/devkitpro/devkitARM/bin:$PATH" >> $GITHUB_ENV
shell: bash
- name: Check devkitPro gcc and g++ versions
run: |
aarch64-none-elf-gcc --version
aarch64-none-elf-g++ --version
shell: bash # is this even needed? but for consistency let's keep using it
- name: Install ccache
run: |
apt-get update
apt-get install -y ccache
shell: bash
# -------------------------------------------------
# Get short commit SHA
# -------------------------------------------------
- name: Set commit SHA & dist
id: vars
run: |
echo "SHORT_SHA=$(echo $GITHUB_SHA | cut -c1-7)" >> $GITHUB_ENV
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)
- name: Prepare build folder
run: |
mkdir -p build
cp -r atmosphere/* build/
- name: Override ldr_process_creation.cpp
run: |
cp -rf Source/Atmosphere/stratosphere/loader/source/* build/stratosphere/loader/source/
- name: Cache ccache
uses: actions/cache@v5.0.3
with:
path: /root/.cache/ccache
key: ccache-${{ runner.os }}-devkitpro-ams-${{ hashFiles('ams_ver.txt') }} # last key was utter garbage, stick to ams versions,
restore-keys: |
ccache-${{ runner.os }}-devkitpro-
- name: Configure ccache
run: |
export CCACHE_DIR=/root/.cache/ccache
echo "CCACHE_DIR=/root/.cache/ccache" >> $GITHUB_ENV
ccache --set-config=max_size=10G
ccache --set-config=compiler_check=content
ccache --zero-stats
- name: Build Libnx
shell: bash
run: |
export CC="ccache aarch64-none-elf-gcc"
export CXX="ccache aarch64-none-elf-g++"
pushd libnx
make -j$(($(nproc) * 4)) install CXX="ccache aarch64-none-elf-g++" CC="ccache aarch64-none-elf-gcc"
popd
- name: Build hoc-clk sysmodule and overlay
shell: bash
run: |
export CC="ccache aarch64-none-elf-gcc"
export CXX="ccache aarch64-none-elf-g++"
ROOT_DIR="$GITHUB_WORKSPACE/Source/hoc-clk"
DIST_DIR="$ROOT_DIR/dist"
mkdir -p "$DIST_DIR"
echo "*** sysmodule ***"
TITLE_ID="$(grep -oP '"title_id":\s*"0x\K(\w+)' \
"$ROOT_DIR/sysmodule/perms.json")"
echo "TITLE_ID: $TITLE_ID"
pushd "$ROOT_DIR/sysmodule"
git config --global --add safe.directory "$GITHUB_WORKSPACE"
make -j$(($(nproc) * 2)) CXX="ccache aarch64-none-elf-g++" CC="ccache aarch64-none-elf-gcc"
popd
mkdir -p "$DIST_DIR/atmosphere/contents/$TITLE_ID/flags"
cp -vf \
"$ROOT_DIR/sysmodule/out/hoc-clk.nsp" \
"$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 ***"
pushd "$ROOT_DIR/overlay"
make -j$(($(nproc) * 2)) CXX="ccache aarch64-none-elf-g++" CC="ccache aarch64-none-elf-gcc"
popd
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 "*** 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"
- name: Build kip
working-directory: build/stratosphere/loader
run: |
export CC="ccache aarch64-none-elf-gcc"
export CXX="ccache aarch64-none-elf-g++"
make -j$(($(nproc) * 4)) CXX="ccache aarch64-none-elf-g++" CC="ccache aarch64-none-elf-gcc"
hactool -t kip1 out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=hoc.kip
cp hoc.kip ../../../dist/atmosphere/kips/hoc.kip
- name: ccache stats
run: ccache --show-stats
# -------------------------------------------------
# Upload ZIP artifact
# -------------------------------------------------
- name: Upload build artifact
uses: actions/upload-artifact@v6
with:
name: horizon-oc-zeus-dist
path: dist/
compression-level: 3

6
.gitmodules vendored
View File

@@ -1,6 +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
branch = main
[submodule "Source/hoc-clk/overlay/lib/libultrahand"]
path = Source/hoc-clk/overlay/lib/libultrahand
url = https://github.com/ppkantorski/libultrahand
branch = main

View File

@@ -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

View File

@@ -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

162
README.md
View File

@@ -1,17 +1,17 @@
<div align="center">
<img src="assets/logo.png" alt="logo" width="350"/>
<img src="assets/logo.png" alt="logo" width="768"/>
---
![License: GPL-2.0](https://img.shields.io/badge/GPL--2.0-red?style=for-the-badge)
![Nintendo Switch](https://img.shields.io/badge/Nintendo_Switch-E60012?style=for-the-badge\&logo=nintendo-switch\&logoColor=white)
[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge\&logo=discord\&logoColor=white)](https://discord.com/invite/S3eX47dHsB)
[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge\&logo=discord\&logoColor=white)](https://dsc.gg/horizonoc)
![VSCode](https://img.shields.io/badge/VSCode-0078D4?style=for-the-badge\&logo=visual%20studio%20code\&logoColor=white)
![Made with Notepad++](assets/np++.png?raw=true)
![C++](https://img.shields.io/badge/C%2B%2B-00599C?style=for-the-badge\&logo=c%2B%2B\&logoColor=white)
![Downloads](https://img.shields.io/github/downloads/souldbminersmwc/Horizon-OC/total.svg?style=for-the-badge)
![Downloads](https://img.shields.io/github/downloads/Horizon-OC/Horizon-OC/total.svg?style=for-the-badge)
---
@@ -32,18 +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
> *Higher (potentially dangerous) frequencies are unlockable via configuration.*
> *Erista and Mariko units can usually push a bit further fully safely with a bit of undervolting, however this may not work on all units.*
> *The exact maximum overclock possible varies per console, although most consoles should be able to do this safely.*
> 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
---
@@ -58,7 +56,7 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
```
kip1=atmosphere/kips/hoc.kip
secmon=exosphere.bin
secmon=atmosphere/exosphere.bin
```
*(No changes needed if using fusee.)*
@@ -69,7 +67,7 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
1. Open the Horizon OC Overlay
2. Open the settings menu
3. Adjust your overclocking settings as desired.
3. Adjust your overclocking settings as desired. A helpful guide can be found [here.](https://rentry.co/mariko#oc-settings-for-horizon-oc)
4. Click **Save KIP Settings** to apply your configuration.
---
@@ -78,21 +76,141 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
Refer to COMPILATION.md
---
## Clock table
### 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.
* 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 (mhz)
* 2703 → mariko absolute max, dangerous
* 2601 → unsafe
* 2499
* 2397 → mariko safe max with UV (low speedo)
* 2295
* 2193
* 2091
* 1963 → mariko no UV max clock
* 1887
* 1785 → erista no UV max clock, boost mode
* 1683
* 1581
* 1428
* 1326
* 1224 → sdev oc
* 1122
* 1020 → official docked & handheld
* 918
* 816
* 714
* 612 → sleep mode
### GPU clocks (mhz)
* 1536 → absolute max clock on mariko. very dangerous
* 1459
* 1382
* 1305
* 1267 → NVIDIA T214(mariko) rating
* 1228 → mariko High UV safe clock
* 1152 → mariko hiOpt-15mV max clock
* 1075 → mariko hiOpt max clock. absolute max clock on erista. very dangerous
* 998 → NVIDIA T210 (erista) rating
* 960 (erista only) → erista high uv/hiOpt-15mV safe max clock
* 921 → erista no UV max clock
* 844
* 768 → official docked
* 691
* 614
* 537
* 460 → max handheld
* 384 → official handheld
* 307 → official handheld
* 230
* 153
* 76 → boost mode
**Notes:**
1. 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
* **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, nautalis)** 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

View File

@@ -4,8 +4,10 @@
| 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
Contact me on discord (soul_9017) or email me (souldbminer@gmail.com)
Contact Souldbminer or Lightos_ on discord (souldbminer, lightos_)

View File

@@ -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"

View File

@@ -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_); \

View File

@@ -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);

View File

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

View File

@@ -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) {

View 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
}
}
]
}

View File

@@ -93,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];
@@ -104,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. */
@@ -114,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);
@@ -174,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. */
@@ -204,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:
@@ -225,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();
@@ -298,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] */
@@ -523,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;
@@ -534,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;
}
}
@@ -607,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());
@@ -620,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);
@@ -692,11 +730,11 @@ namespace ams::ldr {
/* Apply PCV and PTM patches */
if (g_is_pcv) {
oc::pcv::Patch(map_address, nso_size);
hoc::pcv::Patch(map_address, nso_size);
}
if (g_is_ptm) {
oc::ptm::Patch(map_address, nso_size);
hoc::ptm::Patch(map_address, nso_size);
}
}
@@ -709,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. */
@@ -753,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;
@@ -767,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));
}
}
@@ -811,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. */
{
@@ -829,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]);
}
}

View File

@@ -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)

View File

@@ -3,7 +3,7 @@
*
* Copyright (c) 2023 hanai3Bi
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -26,76 +26,127 @@
#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::oc {
namespace ams::ldr::hoc {
volatile CustomizeTable C = {
/* Disables RAM powerdown */
.hpMode = DISABLED,
.commonEmcMemVolt = 1175000, // LPDDR4X JEDEC Specification
.eristaEmcMaxClock = 1600000, // Maximum HB-MGCH ram rating
.commonEmcMemVolt = 1175000, /* LPDDR4(X) JEDEC Specification */
.eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */
.eristaEmcMaxClock1 = 1600000,
.eristaEmcMaxClock2 = 1600000,
.marikoEmcMaxClock = 2133000,
/* Available: 66MHz step rate, 100MHz step rate, 133MHz step rate and jedec. */
/* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */
.stepMode = StepMode_66MHz,
.marikoEmcMaxClock = 2133000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
.marikoEmcVddqVolt = 600000,
.emcDvbShift = 0,
// Primary
.t1_tRCD = 0,
.t2_tRP = 0,
.t3_tRAS = 0,
// Secondary
.t4_tRRD = 0,
.t5_tRFC = 0,
.t6_tRTW = 0,
.t7_tWTR = 0,
.t8_tREFI = 0,
.emcDvbShift = 0,
.marikoSocVmax = 0, /* 0 = stock limits (1450 - 1597 is 1050mV, 1598-1708 is 1025mV, 1709+ is 1000mV). */
/* Set to 4 read and 2 write for 1866b tWRL. */
/* For 2133 tWRL: 8 read and 4 write. */
.mem_burst_read_latency = 8,
.mem_burst_write_latency = 4,
/* 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,
.eristaCpuUV = 0,
.eristaCpuVmin = 800,
/* 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 */
/*
* Read:
* 2133RL = 40
* 1866RL = 36
* 1600RL = 32
* 1331RL = 28
* Write:
* 2133WL = 18
* 1866WL = 16
* 1600WL = 14
* 1331WL = 12
*/
/* Erista only. */
.mem_burst_read_latency = RL_1600,
.mem_burst_write_latency = WL_1600,
.eristaCpuUV = 0,
.eristaCpuVmin = 800,
.eristaCpuMaxVolt = 1200,
/* Unlocks up to 2295 Mhz CPU, usage is not recommended. */
.eristaCpuUnlock = DISABLED,
/* Unlocks up to 2397 Mhz CPU, usage is not recommended. */
.eristaCpuUnlock = DISABLED,
.marikoCpuUVLow = 0, // No undervolt
.marikoCpuUVLow = 0, // No undervolt
.marikoCpuUVHigh = 0, // No undervolt
.tableConf = DEFAULT_TABLE, /* TODO: Add AUTO */
.marikoCpuLowVmin = 620,
.marikoCpuHighVmin = 750,
.marikoCpuMaxVolt = 1120,
/* Supported values: 2397000, 2499000, 2601000, 2703000. */
.tableConf = TBREAK_1683,
.marikoCpuLowVmin = 620,
.marikoCpuHighVmin = 750,
/* 1120mV is NVIDIA rating */
.marikoCpuMaxVolt = 1120,
/* Supported values: 1963500, 2091000, 2193000, 2295000, 2397000, 2499000, 2601000, 2703000. */
/* 1963500 is official rating of T214/Mariko, fully safe. */
/* 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 = 2397000,
.marikoCpuMaxClock = 1963500,
.eristaCpuBoostClock = 1785000, // Default boost clock
.marikoCpuBoostClock = 1963000, // Default boost clock
.eristaCpuBoostClock = 1785000, /* Default boost clock */
.marikoCpuBoostClock = 1963500, /* Default boost clock */
.eristaGpuUV = 0,
.eristaGpuVmin = 800,
.eristaGpuUV = 0,
.eristaGpuVmin = 810,
.marikoGpuUV = 0,
/* For automatic vmin detection, set this to AUTO. */
/* Vmin past 795mV won't work due boot voltage being 800mV (can be adjusted though). */
.marikoGpuVmin = 610,
.marikoGpuBootVolt = 800, /* Used during boot and when temp is <20°C */
.marikoGpuVmax = 800,
.commonGpuVoltOffset = 0,
/* Speedo is automatically set by hoc-clk on first boot */
.gpuSpeedo = 1450,
/* This table is used with a gpu uv mode of 2. */
/* Setting DEACTIVATED_GPU_FREQ on any freq will disable it and all freqs greater than it. (the latter is a bug :/) */
/* AUTO: Voltage is optimally chosen; with commonGpuVoltOffset applied. */
/* AUTO only works up to 1305 GPU */
/* AUTO only works up to 1305 GPU on Mariko and 998 GPU on Erista (it is reccomended to manually set your 998MHz voltage though) */
/* You can overwrite auto with any voltage (in mv) of your choice - offset will not be applied. */
.eristaGpuVoltArray = {
AUTO /* 76 */,
@@ -142,8 +193,8 @@ volatile CustomizeTable C = {
AUTO /* 921 */,
AUTO /* 998 */,
AUTO /* 1075 */,
DEACTIVATED_GPU_FREQ /* 1152 */,
DEACTIVATED_GPU_FREQ /* 1228 */,
AUTO /* 1152 (SLT / HiOPT Only!) */,
AUTO /* 1228 (HiOPT Only!) */,
DEACTIVATED_GPU_FREQ /* 1267 (Disabled by default) */,
DEACTIVATED_GPU_FREQ /* 1305 (Disabled by default) */,
DEACTIVATED_GPU_FREQ /* 1344 (Disabled by default) */,
@@ -154,6 +205,10 @@ volatile CustomizeTable C = {
DEACTIVATED_GPU_FREQ /* 1536 (Disabled by default) */,
},
/* Advanced. */
.fineTune_t6_tRTW = 0,
.fineTune_t7_tWTR = 0,
/* You shouldn't have to anything past here. */
.eristaCpuDvfsTable = {
{ 204000, { 721094, }, { } },
@@ -172,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 = {
@@ -196,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 = {
@@ -310,7 +366,7 @@ volatile CustomizeTable C = {
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
},
.marikoCpuDvfsTableHelios {
.marikoCpuDvfsTableExtreme {
{ 204000, { 732856, -17335, 113, }, { } },
{ 306000, { 760024, -18195, 113, }, { } },
{ 408000, { 789258, -19055, 113, }, { } },
@@ -438,7 +494,7 @@ volatile CustomizeTable C = {
{ 921600, { }, { 970060,-10108, -614,-179, 1508, -13 } },
{ 998400, { }, { 1065665,-16075, -497,-179, 3213, 9 } },
{ 1075200, { }, { 1132576,-16093, -648, 0, 1077, 40 } },
{ 1152000, { }, { 1180029,-14534, -830, 0, 1469, 110 } },
// { 1152000, { }, { 1180029,-14534, -830, 0, 1469, 110 } },
// { 1228800, { }, { 1248293,-16383, -859, 0, 3722, 313 } },
// { 1267200, { }, { 1286399,-17475, -867, 0, 3681, 559 } },
},

View File

@@ -3,7 +3,7 @@
*
* Copyright (c) 2023 hanai3Bi
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -20,25 +20,40 @@
#pragma once
#define CUST_REV 11
#define CUST_REV 4
#define KIP_VERSION 240
#include "oc_common.hpp"
#include "pcv/pcv_common.hpp"
namespace ams::ldr::oc {
#include "mtc_timing_table.hpp"
enum MtcConfig: u32 {
AUTO_ADJ = 0,
AUTO_ADJ_BL = 1,
};
namespace ams::ldr::hoc {
enum TableConfig: u32 {
DEFAULT_TABLE = 1,
TBREAK_1581 = 2,
TBREAK_1683 = 3,
HELIOS_TABLE = 4,
TBREAK_1581 = 2,
TBREAK_1683 = 3,
EXTREME_TABLE = 4,
};
enum StepMode: u32 {
StepMode_66MHz = 0,
StepMode_100MHz = 1,
StepMode_Jedec = 2,
StepMode_133MHz = 3,
};
enum ReadLatency: u32 {
RL_2133 = 40,
RL_1866 = 36,
RL_1600 = 32,
RL_1331 = 28,
};
enum WriteLatency: u32 {
WL_2133 = 18,
WL_1866 = 16,
WL_1600 = 14,
WL_1331 = 12,
};
using CustomizeCpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit];
@@ -46,23 +61,23 @@ using CustomizeGpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit];
static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(CustomizeGpuDvfsTable));
static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(pcv::cvb_entry_t) * pcv::DvfsTableEntryLimit);
constexpr uint32_t ERISTA_MTC_MAGIC = 0x43544D45; // EMTC
constexpr uint32_t MARIKO_MTC_MAGIC = 0x43544D4D; // MMTC
struct CustomizeTable {
u8 cust[4] = {'C', 'U', 'S', 'T'};
u32 custRev = CUST_REV;
u32 kipVersion = KIP_VERSION;
typedef struct CustomizeTable {
u8 cust[4] = {'C', 'U', 'S', 'T'};
u32 custRev = CUST_REV;
u32 mtcConf;
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;
@@ -73,6 +88,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;
@@ -97,16 +121,21 @@ typedef struct CustomizeTable {
u32 marikoGpuUV;
u32 marikoGpuVmin;
u32 marikoGpuBootVolt;
u32 marikoGpuVmax;
u32 commonGpuVoltOffset;
/* TODO: Automatically detect speedo. */
u32 gpuSpeedo;
u32 eristaGpuVoltArray[27];
u32 marikoGpuVoltArray[24];
u32 fineTune_t6_tRTW;
u32 fineTune_t7_tWTR;
u32 reserved[60];
CustomizeCpuDvfsTable eristaCpuDvfsTable;
CustomizeCpuDvfsTable eristaCpuDvfsTableSLT;
@@ -114,7 +143,7 @@ typedef struct CustomizeTable {
CustomizeCpuDvfsTable marikoCpuDvfsTableSLT;
CustomizeCpuDvfsTable marikoCpuDvfsTable1581Tbreak;
CustomizeCpuDvfsTable marikoCpuDvfsTable1683Tbreak;
CustomizeCpuDvfsTable marikoCpuDvfsTableHelios;
CustomizeCpuDvfsTable marikoCpuDvfsTableExtreme;
CustomizeGpuDvfsTable eristaGpuDvfsTable;
CustomizeGpuDvfsTable eristaGpuDvfsTableSLT;
@@ -124,7 +153,7 @@ typedef struct CustomizeTable {
CustomizeGpuDvfsTable marikoGpuDvfsTableSLT;
CustomizeGpuDvfsTable marikoGpuDvfsTableHiOPT;
} CustomizeTable;
};
extern volatile CustomizeTable C;

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) Lightos_
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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 - (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;
const double tMMRI = tRCD + (tCK_avg * 3);
pdex2mrr = tMMRI + 10;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Lightos_
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace ams::ldr::hoc::pcv::erista {
void CalculateTimings(double tCK_avg);
}

View File

@@ -14,100 +14,191 @@
* 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"
namespace ams::ldr::oc::pcv::mariko {
namespace ams::ldr::hoc::pcv::mariko {
u32 GetRext() {
void GetRext() {
if (auto r = FindRext()) {
return r->correct;
rext = r->rext;
return;
}
return 0x1A;
/* > 3200 */
rext = 0x1E;
}
/* TODO: This function is quite uggly, refactor! */
void CalculateMiscTimings() {
einput_duration = 0x1C;
void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) {
latency += index * latencyStep;
}
for (u32 i = 0; i < g_misc_table_size; i++) {
const auto& e = g_misc_table[i];
if (C.marikoEmcMaxClock >= e.min_freq) {
if (e.einput) einput_duration = e.einput;
static s32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) {
s32 maxIndex = -1;
for (u32 i = 0; i < latencySize; ++i) {
if (latencyArray[i]) {
maxIndex = i;
}
}
rext = GetRext();
return maxIndex;
}
void CalculateIbdly() {
/* Ibdly is so inconsistent, I am using the most common value and then checking with a lookup table. */
ibdly = 0x1000001D + C.mem_burst_read_latency;
if (auto patch = FindIbdlyPatch()) {
ibdly += patch->adjust;
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 CalculateTWTPDEN() {
tWTPDEN = tW2P + 1 + CEIL(tDQSS_max / tCK_avg) + CEIL(tDQS2DQ_max / tCK_avg) + 6;
if (C.marikoEmcMaxClock >= 2'233'000 && C.marikoEmcMaxClock < 2'533'000) tWTPDEN++;
if (C.marikoEmcMaxClock >= 2'433'000 && C.marikoEmcMaxClock < 2'800'000) tWTPDEN--;
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 CalculateTR2W() {
tR2W = CEIL(RL_DBI + (tDQSCK_max / tCK_avg) + (BL / 2) - WL + tWPRE + FLOOR(tRPST) + 9.0) - (C.t6_tRTW * 3);
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 (auto patch = FindTR2WPatch()) {
tR2W += patch->adjust;
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 CalculateQsafe() {
qsafe = ROUND((C.marikoEmcMaxClock / 1000.0) / 138.0 + 37.4) + C.mem_burst_read_latency;
if (auto patch = FindQsafePatch()) {
qsafe += patch->adjust;
void CalculateMrw2() {
static const u8 rlMapDBI[8] = {
6, 12, 16, 22, 28, 32, 36, 40
};
static const u8 wlMapSetA[8] = {
4, 6, 8, 10, 12, 14, 16, 18
};
u32 rlIndex = 0;
u32 wlIndex = 0;
for (u32 i = 0; i < std::size(rlMapDBI); ++i) {
if (rlMapDBI[i] == RL) {
rlIndex = i;
break;
}
}
}
void CalculateQpop() {
qpop = FLOOR(((C.marikoEmcMaxClock / 1000.0) - 2133 + 167) / 200.0) + 0x2D + C.mem_burst_read_latency;
if (C.marikoEmcMaxClock >= 3'133'000) qpop++;
}
void CalculatePdex2rw() {
double freq_mhz = C.marikoEmcMaxClock / 1000.0;
double pdex_local = (0.011 * freq_mhz) - 1.443;
pdex2rw = static_cast<u32>(ROUND(pdex_local));
if (pdex2rw < 22) pdex2rw = 22;
if (pdex2rw > 33) pdex2rw = 33;
if (auto patch = FindPdex2rwPatch()) {
pdex2rw += patch->adjust;
for (u32 i = 0; i < std::size(wlMapSetA); ++i) {
if (wlMapSetA[i] == WL) {
wlIndex = i;
break;
}
}
/* DBI is always enabled. */
mrw2 = static_cast<u8>(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6)));
}
void CalculateCke2pden() {
cke2pden = (static_cast<double>((C.marikoEmcMaxClock / 1000.0) * 0.00875) - 0.65);
void CalculateTimings(double tCK_avg, u32 freq) {
RL = RL_1331;
WL = WL_1331;
if (auto patch = FindCke2pdenPatch()) {
cke2pden += patch->adjust;
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);
}
}
void CalculateTimings() {
CalculateMiscTimings();
CalculateIbdly();
CalculateTWTPDEN();
CalculateTR2W();
CalculateQsafe();
CalculateQpop();
CalculatePdex2rw();
CalculateCke2pden();
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();
}
}

View File

@@ -16,8 +16,9 @@
#pragma once
namespace ams::ldr::oc::pcv::mariko {
namespace ams::ldr::hoc::pcv::mariko {
void CalculateTimings();
void CalculateTimings(double tCK_avg, u32 freq);
}

View File

@@ -14,38 +14,10 @@
* 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::oc::pcv::mariko {
const MiscTimings g_misc_table[] = {
{1'866'000, 0x20, },
{2'133'000, 0x24, },
{2'166'000, 0, },
{2'233'000, 0x25, },
{2'300'000, 0x26, },
{2'333'000, 0x27, },
{2'366'000, 0x26, },
{2'433'000, 0x27, },
{2'466'000, 0x2A, },
{2'500'000, 0x28, },
{2'533'000, 0x29, },
{2'566'000, 0, },
{2'633'000, 0x2A, },
{2'700'000, 0x2B, },
{2'733'000, 0x2C, },
{2'766'000, 0x2B, },
{2'833'000, 0x2C, },
{2'866'000, 0, },
{2'900'000, 0, },
{2'933'000, 0x2E, },
{2'966'000, 0, },
{3'033'000, 0x2F, },
{3'133'000, 0x31, },
};
const u32 g_misc_table_size = sizeof(g_misc_table) / sizeof(g_misc_table[0]);
namespace ams::ldr::hoc::pcv::mariko {
const ReplacePatch g_rext_table[] = {
{2'133'000, 0x1A}, {2'166'000, 0x19}, {2'200'000, 0x19},
@@ -64,137 +36,11 @@ namespace ams::ldr::oc::pcv::mariko {
const u32 g_rext_table_size = sizeof(g_rext_table) / sizeof(g_rext_table[0]);
const ReplacePatch *FindRext() {
for (u32 i = 0; i < g_rext_table_size; i++)
if (g_rext_table[i].freq == C.marikoEmcMaxClock)
for (u32 i = 0; i < g_rext_table_size; i++) {
if (g_rext_table[i].freq >= C.marikoEmcMaxClock) {
return &g_rext_table[i];
return nullptr;
}
const AdjustPatch g_ibdly_patches[] = {
{2'133'000, -2},
{2'166'000, -1},
{2'200'000, -1},
{2'233'000, -1},
{2'266'000, -1},
{2'300'000, -2},
{2'333'000, -2},
{2'500'000, -1},
{2'533'000, -2},
{2'566'000, -1},
{2'600'000, -1},
{2'633'000, -1},
{2'666'000, -1},
{2'700'000, -2},
{2'733'000, -2},
{2'933'000, -1},
};
const u32 g_ibdly_table_size = sizeof(g_ibdly_patches) / sizeof(g_ibdly_patches[0]);
const AdjustPatch *FindIbdlyPatch() {
for (u32 i = 0; i < g_ibdly_table_size; i++)
if (g_ibdly_patches[i].freq == C.marikoEmcMaxClock)
return &g_ibdly_patches[i];
return nullptr;
}
const AdjustPatch g_tr2w_patches[] = {
{2'500'000, 1},
{2'533'000, 1},
{2'566'000, 1},
{2'866'000, -1},
{3'100'000, 1},
{3'133'000, 1},
};
const u32 g_tr2w_table_size = sizeof(g_tr2w_patches) / sizeof(g_tr2w_patches[0]);
const AdjustPatch *FindTR2WPatch() {
for (u32 i = 0; i < g_tr2w_table_size; i++)
if (g_tr2w_patches[i].freq == C.marikoEmcMaxClock)
return &g_tr2w_patches[i];
return nullptr;
}
const AdjustPatch g_qsafe_patches[] = {
{2'166'000, 1},
{2'200'000, 1},
{2'500'000, -1},
{2'533'000, -1},
{2'666'000, -1},
{2'700'000, -1},
{2'733'000, -1},
{2'800'000, -1},
{2'833'000, -1},
{2'866'000, -1},
{2'900'000, -1},
{2'933'000, -2},
{2'966'000, -1},
{3'000'000, -1},
{3'033'000, -1},
{3'066'000, -2},
{3'100'000, -2},
{3'166'000, -1},
{3'200'000, -1},
};
const u32 g_qsafe_table_size = sizeof(g_qsafe_patches) / sizeof(g_qsafe_patches[0]);
const AdjustPatch *FindQsafePatch() {
for (u32 i = 0; i < g_qsafe_table_size; i++)
if (g_qsafe_patches[i].freq == C.marikoEmcMaxClock)
return &g_qsafe_patches[i];
return nullptr;
}
const AdjustPatch g_pdex2rw_patches[] = {
{2'166'000, 1},
{2'300'000, 1},
{2'333'000, 1},
{2'433'000, 1},
{2'533'000, 0},
{2'633'000, -1},
{2'666'000, -1},
{2'733'000, -1},
{2'766'000, -1},
{2'800'000, -1},
{2'833'000, -1},
{2'933'000, -1},
{3'066'000, 1},
};
const u32 g_pdex2rw_table_size = sizeof(g_pdex2rw_patches) / sizeof(g_pdex2rw_patches[0]);
const AdjustPatch *FindPdex2rwPatch() {
for (u32 i = 0; i < g_pdex2rw_table_size; i++)
if (g_pdex2rw_patches[i].freq == C.marikoEmcMaxClock)
return &g_pdex2rw_patches[i];
return nullptr;
}
const AdjustPatch g_cke2pden_patches[] = {
{2'133'000, 1},
{2'166'000, 1},
{2'266'000, 1},
{2'300'000, 1},
{2'366'000, 1},
{2'400'000, 1},
{2'500'000, 1},
{2'633'000, 1},
{2'733'000, 1},
{2'833'000, 1},
{2'866'000, 1},
{2'966'000, 1},
{3'066'000, 1},
{3'100'000, 1},
};
const u32 g_cke2pden_table_size = sizeof(g_cke2pden_patches) / sizeof(g_cke2pden_patches[0]);
const AdjustPatch *FindCke2pdenPatch() {
for (u32 i = 0; i < g_cke2pden_table_size; i++)
if (g_cke2pden_patches[i].freq == C.marikoEmcMaxClock)
return &g_cke2pden_patches[i];
}
}
return nullptr;
}

View File

@@ -15,50 +15,17 @@
*/
#pragma once
#include "../mtc_timing_value.hpp"
#include <stratosphere.hpp>
namespace ams::ldr::oc::pcv::mariko {
namespace ams::ldr::hoc::pcv::mariko {
struct ReplacePatch {
u32 freq;
u32 correct;
u32 rext;
};
extern const ReplacePatch g_rext_table[];
extern const u32 g_rext_table_size;
const ReplacePatch *FindRext();
struct AdjustPatch {
u32 freq;
s32 adjust;
};
extern const AdjustPatch g_ibdly_patches[];
extern const u32 g_ibdly_table_size;
const AdjustPatch *FindIbdlyPatch();
extern const AdjustPatch g_tr2w_patches[];
extern const u32 g_tr2w_table_size;
const AdjustPatch *FindTR2WPatch();
extern const AdjustPatch g_qsafe_patches[];
extern const u32 g_qsafe_table_size;
const AdjustPatch *FindQsafePatch();
extern const AdjustPatch g_pdex2rw_patches[];
extern const u32 g_pdex2rw_table_size;
const AdjustPatch *FindPdex2rwPatch();
extern const AdjustPatch g_cke2pden_patches[];
extern const u32 g_cke2pden_table_size;
const AdjustPatch *FindCke2pdenPatch();
struct MiscTimings {
u32 min_freq;
u32 einput;
};
extern const MiscTimings g_misc_table[];
extern const u32 g_misc_table_size;
}

View File

@@ -20,33 +20,22 @@
#include "oc_common.hpp"
namespace ams::ldr::oc {
#define MAX(A, B) std::max(A, B)
#define MIN(A, B) std::min(A, B)
#define CEIL(A) std::ceil(A)
#define FLOOR(A) std::floor(A)
#define ROUND(A) std::lround(A)
namespace ams::ldr::hoc {
#define MAX(A, B) std::max(A, B)
#define MIN(A, B) std::min(A, B)
#define CEIL(A) std::ceil(A)
#define FLOOR(A) std::floor(A)
#define ROUND(A) std::lround(A)
#define PACK_U32(high, low) ((static_cast<uint32_t>(high) << 16) | (static_cast<uint32_t>(low) & 0xFFFF))
/* Primary timings. */
const std::array<u32, 8> tRCD_values = { 18, 17, 16, 15, 14, 13, 12, 11 };
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, 7> tRRD_values = { /*10.0,*/ 7.5, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 }; /* 10.0 is used for <2133mhz; do we care? 8gb uses 7.5 tRRD on >=1331. */
const std::array<u32, 11> tRFC_values = { 140, 130, 120, 110, 100, 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 };
#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 = 28 + C.mem_burst_read_latency;
const u32 WL = 14 + C.mem_burst_write_latency;
/* Switch uses RL_DBI, todo: get rid of non DBI_RL. */
const u32 RL_DBI = RL + 4;
const u32 RL = C.mem_burst_read_latency;
const u32 WL = C.mem_burst_write_latency;
/* Precharge to Precharge Delay. (tCK) */
const u32 tPPD = 4;
@@ -74,13 +63,14 @@ namespace ams::ldr::oc {
/* Write recovery time. */
const u32 tWR = 18;
/* TOOD: Fix erista */
namespace pcv::erista {
}
namespace pcv::mariko {
const double tCK_avg = 1000'000.0 / C.marikoEmcMaxClock;
const std::array<u32, 8> tRCD_values = { 18, 17, 16, 15, 14, 13, 12, 11 };
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, 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 };
const u32 tRCD = tRCD_values[C.t1_tRCD];
const u32 tRPpb = tRP_values[C.t2_tRP];
@@ -88,6 +78,8 @@ namespace ams::ldr::oc {
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;
const u32 tRC = tRAS + tRPpb;
const u32 tRFCab = tRFCpb * 2;
@@ -95,44 +87,82 @@ namespace ams::ldr::oc {
const u32 tFAW = static_cast<u32>(tRRD * 4.0);
const double tRPab = tRPpb + 3;
const u32 tR2P = 12 + (C.mem_burst_read_latency / 2);
const u32 tR2P = CEIL((RL * 0.426) - 2.0);
inline u32 tR2W;
const u32 tRTM = RL + 9 + (tDQSCK_max / tCK_avg) + FLOOR(tRPST) + CEIL(10 / tCK_avg); // Fix?
const u32 tRATM = tRTM + CEIL(10 / tCK_avg) - 12; // Fix?
const u32 rdv = FLOOR(17.02046755653219 + (RL_DBI + ((C.marikoEmcMaxClock / 1000.0) * 0.00510056573299173)));
const u32 quse = FLOOR((-0.0048159 * (C.marikoEmcMaxClock / 1000.0)) + RL_DBI) + (FLOOR((C.marikoEmcMaxClock / 1000.0) * 0.0050997) * 1.5134);
const u32 einput = quse - ((C.marikoEmcMaxClock / 1000.0) * 0.01);
inline u32 einput_duration;
inline u32 ibdly;
const u32 obdly = 0x10000000 + CEIL(MAX((WL + (2.072347067198409 * CEIL(((C.marikoEmcMaxClock / 1000.0) * -0.0008701518090699537) + 1.1926184709583145))) - 12.368815500608948, -1.8792921762826563e-9));
const u32 quse_width = CEIL(((3.7165006256863955 - (C.marikoEmcMaxClock / 1000.0)) + (-0.002446584377651142 * (C.marikoEmcMaxClock / 1000.0))) - FLOOR((C.marikoEmcMaxClock / 1000.0) / -0.9952024303111688));
inline u32 rext;
const u32 qrstHighDuration = FLOOR(((C.marikoEmcMaxClock / 1000.0) * 0.001477125119082522) + 4.272302254983803);
const u32 qrstLow = CEIL(MAX(((C.marikoEmcMaxClock / 1000.0) * -0.010085158701622026) + ((rdv + (-15.612107759528982 - quse_width)) - qrstHighDuration), -0.0004475366008085334));
const u32 qrst = PACK_U32(qrstHighDuration, qrstLow);
inline u32 qsafe;
inline u32 qpop;
const u32 tW2P = (CEIL(WL * 1.7303) * 2) - 5;
const u32 tW2P = (CEIL(WL * 1.7303) * 2) - 5;
inline u32 tWTPDEN;
const u32 tW2R = CEIL(MAX(WL + (0.010322547033278747 * (C.marikoEmcMaxClock / 1000.0)), (WL * -0.2067922202979121) + FLOOR(((RL_DBI * -0.1331159971685554) + WL) * 3.654131957826108)) - (tWTR / tCK_avg));
const u32 tWTM = WL + (BL / 2) + 1 + CEIL(7.5 / tCK_avg);
const u32 tWATM = tWTM + CEIL(tWR / tCK_avg);
const u32 wdv = WL;
const u32 wsv = WL - 2;
const u32 wev = 0xA + C.mem_burst_write_latency;
inline u32 tW2R;
inline u32 pdex2rw;
inline u32 cke2pden;
const u32 tCKE = CEIL(1.0795 * CEIL(0.0074472 * (C.marikoEmcMaxClock / 1000.0)));
inline u32 tCLKSTOP;
const double tMMRI = tRCD + (tCK_avg * 3);
const double pdex2mrr = tMMRI + 10; /* Do this properly? */
inline double pdex2mrr;
}
namespace pcv::mariko {
const std::array<u32, 8> tRCD_values = { 18, 17, 16, 15, 14, 13, 12, 11 };
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, 7> tRRD_values = { /*10.0,*/ 7.5, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 }; /* 10.0 is used for <2133mhz; do we care? 8gb uses 7.5 tRRD on >=1331. */
const std::array<u32, 11> tRFC_values = { 140, 130, 120, 110, 100, 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 };
inline u32 tRCD;
inline u32 tRPpb;
inline u32 tRAS;
inline double tRRD;
inline u32 tRFCpb;
inline u32 tRC;
inline u32 tRFCab;
inline double tXSR;
inline u32 tFAW;
inline double tRPab;
inline u32 RL;
inline u32 WL;
inline u32 tR2P;
inline u32 tR2W;
inline u32 tRTM;
inline u32 tRATM;
inline u32 rext;
inline u32 rdv;
inline u32 qpop;
inline u32 quse_width;
inline u32 quse;
inline u32 einput_duration;
inline u32 einput;
inline u32 qrst;
inline u32 ibdly;
inline u32 qsafe;
inline u32 tW2P;
inline u32 tWTPDEN;
inline u32 tW2R;
inline u32 tWTM;
inline u32 tWATM;
inline u32 wdv;
inline u32 wsv;
inline u32 wev;
inline u32 obdly;
inline u32 pdex2rw;
inline u32 tCLKSTOP;
inline u32 pdex2mrr;
inline u8 mrw2;
}
}

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -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,9 +50,12 @@ 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::oc {
namespace ams::ldr::hoc {
template<typename Pointer>
struct PatcherEntry {
using patternFn = bool(*)(Pointer* ptr);
@@ -63,40 +66,65 @@ namespace ams::ldr::oc {
size_t maximum_patched_count = 0;
patternFn pattern_search_fn = nullptr;
Pointer value_search;
size_t patched_count = 0;
Result Apply(Pointer* ptr) {
Result Apply(Pointer *ptr) {
Result res = patcher_fn(ptr);
if (R_SUCCEEDED(res))
if (R_SUCCEEDED(res)) {
patched_count++;
}
return res;
}
Result SearchAndApply(Pointer* ptr) {
Result SearchAndApply(Pointer *ptr) {
bool searchOk = false;
if (pattern_search_fn) {
if (pattern_search_fn(ptr)) searchOk = true;
if (pattern_search_fn(ptr)) {
searchOk = true;
}
} else {
if (value_search == *(ptr)) searchOk = true;
if (value_search == *(ptr)) {
searchOk = true;
}
}
if (searchOk)
if (searchOk) {
return Apply(ptr);
}
R_THROW(ldr::ResultUnsuccessfulPatcher());
}
Result CheckResult() {
#ifndef ATMOSPHERE_IS_STRATOSPHERE
R_UNLESS(patched_count > 0, ldr::ResultUnsuccessfulPatcher());
#endif
if (maximum_patched_count)
if (maximum_patched_count) {
R_UNLESS(patched_count <= maximum_patched_count, ldr::ResultUnsuccessfulPatcher());
}
R_SUCCEED();
}
};
namespace panic {
/* Requires modifying g_ams_handlers in secmon_smc_handler.cpp */
constexpr inline void SmcError(u32 rgb) {
SecmonArgs args = {};
constexpr u32 SmcShowErrorID = 0xF0000005;
args.X[0] = SmcShowErrorID;
args.X[1] = rgb;
svcCallSecureMonitor(&args);
}
constexpr inline u32 PackCode(u32 r, u32 g, u32 b) {
return ((r & 0xF) << 8) | ((g & 0xF) << 4) | ((b & 0xF) << 0);
}
constexpr u32 Gpu = PackCode(0xF, 0x7, 0x0);
constexpr u32 Cpu = PackCode(0xF, 0x0, 0x0);
constexpr u32 Emc = PackCode(0x0, 0xF, 0xF);
constexpr u32 Patch = PackCode(0x8, 0x0, 0xF);
}
}

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,

View 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
}

View 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();
}

View File

@@ -1,214 +0,0 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) Souldbminer and Horizon OC Contributors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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::oc::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::oc::C.marikoCpuDvfsTableSLT)) == 25);
// Customized table default
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.eristaCpuDvfsTable)) == 19);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.marikoCpuDvfsTable)) == 21);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.marikoCpuDvfsTableSLT)) == 22);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.eristaGpuDvfsTable)) == 12);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.marikoGpuDvfsTable)) == 17);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.marikoGpuDvfsTableSLT)) == 17);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.marikoGpuDvfsTableHiOPT)) == 17);
constexpr size_t limit = ams::ldr::oc::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::oc::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::oc::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::oc::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::oc::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

View File

@@ -1,81 +0,0 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) Souldbminer and Horizon OC Contributors
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#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

View File

@@ -3,7 +3,7 @@
*
* Copyright (c) 2023 hanai3Bi
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -20,156 +20,159 @@
#include "pcv.hpp"
namespace ams::ldr::oc::pcv {
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
);
R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry());
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() {
struct sValidator {
volatile u32 value;
u32 min;
u32 max;
u32 panic;
bool value_required = false;
Result check() {
if (!value_required && !value) {
R_SUCCEED();
}
if (min && value < min) {
R_THROW(ldr::ResultSafetyCheckFailure());
}
if (max && value > max) {
R_THROW(ldr::ResultSafetyCheckFailure());
}
R_SUCCEED();
}
};
u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
u32 marikoCpuDvfsMaxFreq;
if (C.marikoCpuUVHigh) {
marikoCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq);
} else {
marikoCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
}
u32 eristaGpuDvfsMaxFreq;
switch (C.eristaGpuUV) {
case 0:
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
break;
case 1:
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq);
break;
case 2:
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq);
break;
default:
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
break;
}
u32 marikoGpuDvfsMaxFreq;
switch (C.marikoGpuUV) {
case 0:
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
break;
case 1:
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq);
break;
case 2:
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq);
break;
default:
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
break;
}
sValidator validators[] = {
{ C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true },
{ C.marikoCpuBoostClock, 1020'000, 2703'000, panic::Cpu, true },
{ C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, },
{ C.marikoCpuMaxVolt, 1000, 1200, panic::Cpu, },
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, },
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000, panic::Cpu, },
{ C.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, }, /* Official vmax for the RAMs is 1400-1500mV */
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000, panic::Emc, },
{ C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, },
{ C.marikoEmcVddqVolt, 400'000, 750'000, panic::Emc, },
{ C.marikoSocVmax, 1000, 1200, panic::Emc, },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, panic::Gpu, },
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000, panic::Gpu, },
{ C.marikoGpuVmax, 800, 960, panic::Gpu, },
};
for (auto &v : validators) {
if (R_FAILED(v.check())) {
panic::SmcError(v.panic);
CRASH("Validation FAIL");
}
}
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
SafetyCheck();
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
if (isMariko) {
mariko::Patch(mapped_nso, nso_size);
} else {
marikoCpuDvfsMaxFreq = static_cast<u32>(
GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq
);
erista::Patch(mapped_nso, nso_size);
}
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:
case 3:
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq);
break;
default:
eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
break;
}
u32 marikoGpuDvfsMaxFreq;
switch (C.marikoGpuUV) {
case 0:
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
break;
case 1:
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq);
break;
case 2:
case 3:
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq);
break;
default:
marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
break;
}
sValidator validators[] = {
{ C.eristaCpuBoostClock, 1020'000, 2295'000, true },
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true },
{ C.commonEmcMemVolt, 1100'000, 1350'000 }, // Official burst vmax for the RAMs is 1500mV
{ C.eristaCpuMaxVolt, 1100, 1257 },
{ C.eristaEmcMaxClock, 1600'000, 2600'200 },
{ C.marikoCpuMaxVolt, 1100, 1235 },
{ C.marikoEmcMaxClock, 1600'000, 3500'000 },
{ C.marikoEmcVddqVolt, 550'000, 700'000 },
{ eristaCpuDvfsMaxFreq, 1785'000, 2295'000 },
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000 },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000 },
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000 },
};
for (auto& i : validators) {
if (R_FAILED(i.check()))
CRASH("Validation FAIL");
}
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
#ifdef ATMOSPHERE_IS_STRATOSPHERE
SafetyCheck();
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
if (isMariko)
mariko::Patch(mapped_nso, nso_size);
else
mariko::Patch(mapped_nso, nso_size);
#endif
}
}

View File

@@ -3,7 +3,7 @@
*
* Copyright (c) 2023 hanai3Bi
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -22,267 +22,10 @@
#include "../oc_common.hpp"
#include "pcv_common.hpp"
#include "pcv_erista.hpp"
#include "pcv_mariko.hpp"
namespace ams::ldr::oc::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[] = { 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[] {
{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 {
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, 9, 8 };
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[] = { 1150, 1000, 100, 1000, 10, };
static const u32 gpuVoltDvfsOffsets[] = { 1, 2, 3, 4, 5, };
static_assert(sizeof(gpuVoltDvfsPattern) == sizeof(gpuVoltDvfsOffsets), "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);
}
namespace ams::ldr::hoc::pcv {
inline auto MatchesPattern = [](u32 *base, const auto &offsets, const auto &values) {
for (size_t i = 0; i < std::size(values); ++i) {
@@ -308,8 +51,8 @@ namespace ams::ldr::oc::pcv {
customize_table = const_cast<cvb_entry_t *>(C.marikoCpuDvfsTable1581Tbreak);
break;
}
case HELIOS_TABLE: {
customize_table = const_cast<cvb_entry_t *>(C.marikoCpuDvfsTableHelios);
case EXTREME_TABLE: {
customize_table = const_cast<cvb_entry_t *>(C.marikoCpuDvfsTableExtreme);
break;
}
case DEFAULT_TABLE:
@@ -375,34 +118,34 @@ namespace ams::ldr::oc::pcv {
cvb_entry_t *customize_table;
if (isMariko) {
switch (C.marikoGpuUV) {
case 0:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
break;
case 1:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableSLT);
break;
case 2:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
break;
default:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
break;
}
case 0:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
break;
case 1:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableSLT);
break;
case 2:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
break;
default:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
break;
}
} else {
switch (C.eristaGpuUV) {
case 0:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
break;
case 1:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableSLT);
break;
case 2:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableHiOPT);
break;
default:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
break;
}
case 0:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
break;
case 1:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableSLT);
break;
case 2:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableHiOPT);
break;
default:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
break;
}
}
size_t default_entry_count = GetDvfsTableEntryCount(default_table);
@@ -419,27 +162,26 @@ namespace ams::ldr::oc::pcv {
std::memcpy(gpu_cvb_table_head, (void *)customize_table, customize_table_size);
// Patch GPU volt
if (C.marikoGpuUV == 2 || C.eristaGpuUV == 2) {
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
for (size_t i = 0; i < customize_entry_count; ++i) {
if (isMariko) {
if (C.marikoGpuVoltArray[i] != 0) {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (C.marikoGpuVoltArray[i] * 1000));
ClearCvbPllEntry(entry);
} else {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
}
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
for (size_t i = 0; i < customize_entry_count; ++i) {
if (isMariko) {
if (C.marikoGpuVoltArray[i] != 0) {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (C.marikoGpuVoltArray[i] * 1000));
ClearCvbPllEntry(entry);
} else {
if (C.eristaGpuVoltArray[i] != 0) {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (C.eristaGpuVoltArray[i] * 1000));
ClearCvbPllEntry(entry);
} else {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
}
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
}
} else {
if (C.eristaGpuVoltArray[i] != 0) {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (C.eristaGpuVoltArray[i] * 1000));
ClearCvbPllEntry(entry);
} else {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
}
++entry;
}
} else if (C.commonGpuVoltOffset) {
++entry;
}
if (C.commonGpuVoltOffset && !(isMariko ? C.marikoGpuUV : C.eristaGpuUV)) {
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
for (size_t i = 0; i < customize_entry_count; ++i) {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
@@ -453,26 +195,6 @@ namespace ams::ldr::oc::pcv {
Result MemFreqPllmLimit(u32 *ptr);
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
template <typename T>
Result MemMtcCustomizeTable(T *dst, T *src) {
constexpr u32 mtc_magic = std::is_same_v<T, MarikoMtcTable> ? MARIKO_MTC_MAGIC : ERISTA_MTC_MAGIC;
R_UNLESS(src->rev == mtc_magic, ldr::ResultInvalidMtcMagic());
constexpr u32 ZERO_VAL = UINT32_MAX;
// Skip params from dvfs_ver to clock_src;
for (size_t offset = offsetof(T, clk_src_emc); offset < sizeof(T); offset += sizeof(u32)) {
u32 *src_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(src) + offset);
u32 *dst_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(dst) + offset);
u32 src_val = *src_ent;
if (src_val){
PATCH_OFFSET(dst_ent, src_val == ZERO_VAL ? 0 : src_val);
}
}
R_SUCCEED();
};
void SafetyCheck();
void Patch(uintptr_t mapped_nso, size_t nso_size);

View 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);
};
}

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -16,153 +16,170 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#pragma once
namespace ams::ldr::oc::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 CvbCpuDfllData {
u32 tune0_low;
u32 tune0_high;
u32 tune1_low;
u32 tune1_high;
u32 tune_high_min_millivolts;
u32 tune_high_margin_millivolts;
u64 dvco_calibration_max;
};
typedef struct __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;
struct __attribute__((packed)) CvbMeta {
u64 socType;
CvbCpuDfllData dfllData; /* Maybe? */
u32 unkZero2[6];
u32 unkMagic;
u32 unkZero3;
u32 highVmin;
u32 unkStepMaybe;
u32 vmin;
u32 unkZero4[3];
u32 pllMinMilliVolts;
u32 vmax;
u32 unkScale2;
u32 speedoScale;
u32 voltageScale;
u32 unkZero5;
};
static_assert(sizeof(CvbMeta) == 0x78);
constexpr u32 EmcClkOSLimit = 1600'000;
constexpr u32 CpuClkOSLimit = 1785'000;
constexpr u32 GpuClkOsLimit = 921'600;
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) {
u8 *m = reinterpret_cast<u8 *>(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;
}
}
}

View File

@@ -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;
};
const inline MtcDramIndex mtcIndexTable[] = {
{ ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, },
{ ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC, T210SdevEmcDvfsTableS4gb01, },
{ ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, },
{ ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, },
};
void Patch(uintptr_t mapped_nso, size_t nso_size);
}

View File

@@ -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 CpuHighVminOfficial = 850;
constexpr u32 CpuVminOfficial = 620;
constexpr u32 CpuTune0Low = 0xFFCF;
static const u32 cpuVoltagePatchValues[] = { 850, 38, 1120, 1000, 100, 1000, 0 };
static const s32 cpuVoltagePatchOffsets[] = { -2, -1, 5, 6, 7, 8, 9 };
static_assert(sizeof(cpuVoltagePatchValues) == sizeof(cpuVoltagePatchOffsets), "Invalid cpuVoltagePatch size");
static const u32 cpuVoltThermalData[] = { 620, 1120, 20000, 620, 1120, 70000, 950, 1132, 0, 950, 1227, 0 };
static const u32 allowedCpuMaxFrequencies[] = { 1'963'500, 2'091'000, 2'193'000, 2'295'000, 2'397'000, 2'499'000, 2'601'000, 2'703'000, };
constexpr cvb_entry_t GpuCvbTableDefault[] = {
// GPUB01_NA_CVB_TABLE
{ 76800, {}, { 610000, } },
{ 153600, {}, { 610000, } },
{ 230400, {}, { 610000, } },
{ 307200, {}, { 610000, } },
{ 384000, {}, { 610000, } },
{ 460800, {}, { 610000, } },
{ 537600, {}, { 801688, -10900, -163, 298, -10599, 162, } },
{ 614400, {}, { 824214, -5743, -452, 238, -6325, 81, } },
{ 691200, {}, { 848830, -3903, -552, 119, -4030, -2, } },
{ 768000, {}, { 891575, -4409, -584, 0, -2849, 39, } },
{ 844800, {}, { 940071, -5367, -602, -60, -63, -93, } },
{ 921600, {}, { 986765, -6637, -614, -179, 1905, -13, } },
{ 998400, {}, { 1098475, -13529, -497, -179, 3626, 9, } },
{ 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40, } },
{ 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110, } },
{ 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313, } },
{ 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559, } },
{ },
};
constexpr u32 GpuClkPllMax = 1300'000'000;
constexpr u32 GpuClkPllLimit = 2'600'000;
constexpr u32 GpuVminOfficial = 610;
static const u32 gpuDVFSPattern[] = { 1050, 1000, 100, 1000, 10, };
static const u32 gpuVoltThermalPattern[] = { 800, 1120, 0, 610, 1120, 20000, 610, 1120, 30000, 610, 1120, 50000, 610, 1120, 70000, 610, 1120, 90000, };
static_assert(sizeof(gpuVoltThermalPattern) == 72, "Invalid gpuVoltThermalPattern");
/* GPU Max Clock asm Pattern:
*
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
*
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
*/
inline constexpr u32 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 };
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]);
}
struct DvbEntry {
u64 freq;
u32 volt[4] = {};
};
constexpr DvbEntry EmcDvbTableDefault[] = {
{ 204000, { 637, 637, 637, } },
{ 408000, { 637, 637, 637, } },
{ 800000, { 637, 637, 637, } },
{ 1065600, { 637, 637, 637, } },
{ 1331200, { 650, 637, 637, } },
{ 1600000, { 675, 650, 637, } },
};
/* Movz */
/*
SF | OPC | HW | Imm16 | RD
31 | 30 29 28 27 26 25 24 23 | 22 21 | 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 | 4 3 2 1 0
*/
constexpr u32 SocVoltCompareSpeedoAsm = 0x7118FAFF; /* subs imm, compares to >=1598 max speedo and then goes down process id 1 route. */
constexpr u32 SocVoltWriteProcessIdAsm = 0x2A1F03F4; /* orr, writes id 0. */
constexpr u32 SocVoltWriteVoltageAsm = 0x52808358; /* Movz imm, writes 1050mV. */
constexpr u32 SocVoltSelectRegisterAsm = 0x1A9A3118; /* Csel, selects the voltage -- we need the register of this. */
constexpr u32 SocVoltMultiplyVoltsAsm = 0x1B1A7F0B; /* Mul, converts from mV -> uV */
constexpr u32 SocVoltValidateLimitAsm = 0x6B0A017F; /* Subs, checks limits */
constexpr u32 SocVoltBranchToAbortAsm = 0x540020AC; /* B.ge Branches to abort if limits are invalid. */
ALWAYS_INLINE bool SocVoltPatternFn(u32 *ptr) {
return asm_compare_no_rd(*ptr, SocVoltCompareSpeedoAsm);
}
constexpr u32 SocVoltLimitOfficial = 1050;
constexpr u32 SocVoltLimitMaxDefaultIndex = 17;
static const u32 socVoltLimitArray[DvfsTableEntryCount] = { 637, 650, 675, 700, 725, 750, 775, 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, };
constexpr u32 EmcListDefault[] = { 204000, 1331200, 1600000, };
constexpr u32 EmcListSizeDefault = std::size(EmcListDefault);
constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1;
constexpr u32 EmcRateStep = 33'000;
constexpr u32 EmcRateStepScale = 33'200;
constexpr u32 EmcClkOSAlt = 1331'200;
constexpr u32 EmcClkPllmLimit = 2133'000'000;
constexpr u32 EmcVddqDefault = 600'000;
constexpr u32 MemVdd2Default = 1100'000;
constexpr u32 MTC_TABLE_REV = 3;
constexpr u32 MtcTableCountDefault = 3;
constexpr size_t MtcFullTableSize = sizeof(MarikoMtcTable) * MtcTableCountDefault;
constexpr u32 MtcFullTableCount = 17;
/* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
enum DramId : u64 {
HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 3,
AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 5,
IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 6,
IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 8,
IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 9,
IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 10,
IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE = 11,
HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 12,
HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 13,
HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 14,
HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE = 15,
IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 17,
IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 18,
HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 19,
IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 20,
HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 21,
AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 22,
HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 23,
AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 24,
IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 25,
HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF = 26,
AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 27,
AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 28,
IOWA_4GB_HYNIX_H54G46CYRBX267 = 29,
HOAG_4GB_HYNIX_H54G46CYRBX267 = 30,
AULA_4GB_HYNIX_H54G46CYRBX267 = 31,
IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 32,
HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB = 33,
AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 34,
};
enum MtcTableIndex {
T210b0SdevEmcDvfsTableS4gb01 = 0, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableS4gb03 = 1, /* Samsung AM-MGCJ 4Gb */
T210b0SdevEmcDvfsTableS8gb03 = 2, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableH4gb03 = 3, /* Hynix NME 4Gb */
T210b0SdevEmcDvfsTableM4gb03 = 4, /* Micron WT:F 4Gb */
T210b0SdevEmcDvfsTableS4gbY01 = 5, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableS1y4gbY01 = 6, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableS1y8gbY01 = 7, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableS1y4gbX03 = 8, /* Samsung AA-MGCL 4Gb */
T210b0SdevEmcDvfsTableS1y8gbX03 = 9, /* Samsung AA-MGCL 8Gb */
T210b0SdevEmcDvfsTableS1y4gb01 = 10, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableM1y4gb01 = 11, /* Micron WT:E 4Gb */
T210b0SdevEmcDvfsTableH1y4gb01 = 12, /* Hynix NEE 4Gb */
T210b0SdevEmcDvfsTableS1y8gb04 = 13, /* Samsung AM-MGCJ 8Gb */
T210b0SdevEmcDvfsTableS1z4gb01 = 14, /* Samsung AB-MGCL 4Gb */
T210b0SdevEmcDvfsTableH1a4gb01 = 15, /* Hynix x267 4Gb */
T210b0SdevEmcDvfsTableM1a4gb01 = 16, /* Micron WT:B 8Gb */
MtcTableIndex_Invalid = 17,
};
struct MtcDramIndex {
DramId dramId;
MtcTableIndex index;
};
const inline MtcDramIndex mtcIndexTable[] = {
{ HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
{ AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
{ IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
{ IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, T210b0SdevEmcDvfsTableS4gb03, },
{ IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, T210b0SdevEmcDvfsTableS1y8gb04, },
{ IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME, T210b0SdevEmcDvfsTableH4gb03, },
{ IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE, T210b0SdevEmcDvfsTableM1y4gb01, },
{ HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, T210b0SdevEmcDvfsTableS4gb03, },
{ HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, T210b0SdevEmcDvfsTableS1y8gb04, },
{ HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME, T210b0SdevEmcDvfsTableH4gb03, },
{ HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE, T210b0SdevEmcDvfsTableM1y4gb01, },
{ IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
{ IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
{ HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
{ IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1z4gb01, },
{ HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1y8gb04, },
{ AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1y8gb04, },
{ HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
{ AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
{ IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
{ HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
{ AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
{ AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
{ IOWA_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
{ HOAG_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
{ AULA_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
{ IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
{ HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
{ AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
};
/*
710006abfc 40 01 1f d6 br x10
*/
/*
710006ac28 a0 03 00 90 adrp x0,0x71000de000
710006ac2c 00 80 16 91 add x0=>SdevEmcDvfsTableS4gb01,x0,#0x5a0
*/
/* Br */
/*
| Z | OP | Fixed | A | M | RN | RM
31 30 29 28 27 26 25 | 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 |11 | 10 | 9 8 7 6 5 | 4 3 2 1 0
1 1 0 1 0 1 1 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 Rn 0 0 0 0 0
Z op A M Rm
*/
/* Adrp */
/*
OP | ImmLow | | ImmHigh | RD
31 | 30 29 | 28 27 26 25 24 | 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 | 4 3 2 1 0
*/
/* ADD (immediate) */
/*
SF | OP | S | Fixed value | Sh | Imm12 | RN | RD
31 | 30 | 29 | 28 27 26 25 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0
*/
constexpr u32 MtcBrAsm = 0xD61F0140;
constexpr u32 MtcMovAsm = 0x52800068;
constexpr u32 MtcAdrpAsm = 0x900003A0;
constexpr u32 MtcAddAsm = 0x91168000;
ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) {
/* This builds an address that gets returned, so the register must be x0 by convention. */
return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
}
void Patch(uintptr_t mapped_nso, size_t nso_size);
}

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -18,118 +18,105 @@
#include "ptm.hpp"
namespace ams::ldr::oc::ptm {
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);
}
}
}

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -20,28 +20,28 @@
#include "../oc_common.hpp"
namespace ams::ldr::oc::ptm {
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);
}

View File

@@ -0,0 +1,2 @@
-CSn
/home/sould/Documents/GitHub/Horizon-OC/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.elf

View File

@@ -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)\""

View File

@@ -0,0 +1 @@
Thanks to NaGa for Status Monitor Pro!

File diff suppressed because it is too large Load Diff

View File

@@ -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,13 +439,13 @@ public:
if (SaltySD) {
LoadSharedMemoryAndRefreshRate();
}
if (sysclkIpcRunning() && R_SUCCEEDED(sysclkIpcInitialize())) {
uint32_t sysClkApiVer = 0;
sysclkIpcGetAPIVersion(&sysClkApiVer);
if (sysClkApiVer < 4) {
sysclkIpcExit();
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
uint32_t hocClkApiVer = 0;
hocclkIpcGetAPIVersion(&hocClkApiVer);
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
hocclkIpcExit();
}
else sysclkCheck = 0;
else hocclkCheck = 0;
}
if (R_SUCCEEDED(splInitialize())) {
u64 sku = 0;
@@ -473,13 +466,11 @@ public:
virtual void exitServices() override {
CloseThreads();
if (R_SUCCEEDED(sysclkCheck)) {
sysclkIpcExit();
if (R_SUCCEEDED(hocclkCheck)) {
hocclkIpcExit();
}
shmemClose(&_sharedmemory);
//Exit services
clkrstExit();
pcvExit();
tsExit();
tcExit();
pwmChannelSessionClose(&g_ICon);
@@ -507,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");
@@ -530,13 +519,13 @@ public:
if (SaltySD) {
LoadSharedMemory();
}
if (sysclkIpcRunning() && R_SUCCEEDED(sysclkIpcInitialize())) {
uint32_t sysClkApiVer = 0;
sysclkIpcGetAPIVersion(&sysClkApiVer);
if (sysClkApiVer < 4) {
sysclkIpcExit();
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
uint32_t hocClkApiVer = 0;
hocclkIpcGetAPIVersion(&hocClkApiVer);
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
hocclkIpcExit();
}
else sysclkCheck = 0;
else hocclkCheck = 0;
}
if (R_SUCCEEDED(splInitialize())) {
u64 sku = 0;
@@ -557,12 +546,10 @@ public:
virtual void exitServices() override {
CloseThreads();
shmemClose(&_sharedmemory);
if (R_SUCCEEDED(sysclkCheck)) {
sysclkIpcExit();
if (R_SUCCEEDED(hocclkCheck)) {
hocclkIpcExit();
}
//Exit services
clkrstExit();
pcvExit();
tsExit();
tcExit();
pwmChannelSessionClose(&g_ICon);
@@ -594,8 +581,6 @@ public:
// Same serviceinit 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");
@@ -617,13 +602,13 @@ public:
if (SaltySD) {
LoadSharedMemory();
}
if (sysclkIpcRunning() && R_SUCCEEDED(sysclkIpcInitialize())) {
uint32_t sysClkApiVer = 0;
sysclkIpcGetAPIVersion(&sysClkApiVer);
if (sysClkApiVer < 4) {
sysclkIpcExit();
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
uint32_t hocClkApiVer = 0;
hocclkIpcGetAPIVersion(&hocClkApiVer);
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
hocclkIpcExit();
}
else sysclkCheck = 0;
else hocclkCheck = 0;
}
if (R_SUCCEEDED(splInitialize())) {
u64 sku = 0;
@@ -645,12 +630,10 @@ public:
virtual void exitServices() override {
CloseThreads();
shmemClose(&_sharedmemory);
if (R_SUCCEEDED(sysclkCheck)) {
sysclkIpcExit();
if (R_SUCCEEDED(hocclkCheck)) {
hocclkIpcExit();
}
// Exit services
clkrstExit();
pcvExit();
tsExit();
tcExit();
pwmChannelSessionClose(&g_ICon);
@@ -684,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");
@@ -707,13 +688,13 @@ public:
if (SaltySD) {
LoadSharedMemoryAndRefreshRate();
}
if (sysclkIpcRunning() && R_SUCCEEDED(sysclkIpcInitialize())) {
uint32_t sysClkApiVer = 0;
sysclkIpcGetAPIVersion(&sysClkApiVer);
if (sysClkApiVer < 4) {
sysclkIpcExit();
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
uint32_t hocClkApiVer = 0;
hocclkIpcGetAPIVersion(&hocClkApiVer);
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
hocclkIpcExit();
}
else sysclkCheck = 0;
else hocclkCheck = 0;
}
if (R_SUCCEEDED(splInitialize())) {
u64 sku = 0;
@@ -734,11 +715,9 @@ public:
virtual void exitServices() override {
CloseThreads();
shmemClose(&_sharedmemory);
if (R_SUCCEEDED(sysclkCheck)) {
sysclkIpcExit();
if (R_SUCCEEDED(hocclkCheck)) {
hocclkIpcExit();
}
clkrstExit();
pcvExit();
tsExit();
tcExit();
pwmChannelSessionClose(&g_ICon);
@@ -767,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");
@@ -790,13 +767,13 @@ public:
if (SaltySD) {
LoadSharedMemoryAndRefreshRate();
}
if (sysclkIpcRunning() && R_SUCCEEDED(sysclkIpcInitialize())) {
uint32_t sysClkApiVer = 0;
sysclkIpcGetAPIVersion(&sysClkApiVer);
if (sysClkApiVer < 4) {
sysclkIpcExit();
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
uint32_t hocClkApiVer = 0;
hocclkIpcGetAPIVersion(&hocClkApiVer);
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
hocclkIpcExit();
}
else sysclkCheck = 0;
else hocclkCheck = 0;
}
if (R_SUCCEEDED(splInitialize())) {
u64 sku = 0;
@@ -817,11 +794,9 @@ public:
virtual void exitServices() override {
CloseThreads();
shmemClose(&_sharedmemory);
if (R_SUCCEEDED(sysclkCheck)) {
sysclkIpcExit();
if (R_SUCCEEDED(hocclkCheck)) {
hocclkIpcExit();
}
clkrstExit();
pcvExit();
tsExit();
tcExit();
pwmChannelSessionClose(&g_ICon);
@@ -850,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");
@@ -873,13 +846,13 @@ public:
if (SaltySD) {
LoadSharedMemoryAndRefreshRate();
}
if (sysclkIpcRunning() && R_SUCCEEDED(sysclkIpcInitialize())) {
uint32_t sysClkApiVer = 0;
sysclkIpcGetAPIVersion(&sysClkApiVer);
if (sysClkApiVer < 4) {
sysclkIpcExit();
if (hocclkIpcRunning() && R_SUCCEEDED(hocclkIpcInitialize())) {
uint32_t hocClkApiVer = 0;
hocclkIpcGetAPIVersion(&hocClkApiVer);
if (hocClkApiVer != HOCCLK_IPC_API_VERSION) {
hocclkIpcExit();
}
else sysclkCheck = 0;
else hocclkCheck = 0;
}
if (R_SUCCEEDED(splInitialize())) {
u64 sku = 0;
@@ -900,11 +873,9 @@ public:
virtual void exitServices() override {
CloseThreads();
shmemClose(&_sharedmemory);
if (R_SUCCEEDED(sysclkCheck)) {
sysclkIpcExit();
if (R_SUCCEEDED(hocclkCheck)) {
hocclkIpcExit();
}
clkrstExit();
pcvExit();
tsExit();
tcExit();
pwmChannelSessionClose(&g_ICon);
@@ -986,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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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,18 +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));
}
if (R_SUCCEEDED(sysclkCheck)) {
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));
}
}
@@ -291,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) {
@@ -445,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);
@@ -460,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

View File

@@ -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);

View File

@@ -1,5 +1,21 @@
/*
* --------------------------------------------------------------------------
* 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
@@ -8,121 +24,146 @@
* --------------------------------------------------------------------------
*/
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include <sysclk/client/ipc.h>
#include <switch.h>
#include <string.h>
#include <stdatomic.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;
const 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 serviceDispatchInOut(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid, *out_profiles);
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 serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues, *out_configValues);
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 serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues, *configValues);
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)}},
);
}
Result hocClkIpcSetKipData()
{
u32 temp = 0;
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_SetKipData, temp);
}
Result hocClkIpcGetKipData()
{
u32 temp = 0;
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetKipData, temp);
}

View File

@@ -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();
}
}

View 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();
}
}

View 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();
}
}

View File

@@ -1,287 +0,0 @@
diff --git a/Overlay/Makefile b/Overlay/Makefile
index 9656834..3b2ebd5 100644
--- a/Overlay/Makefile
+++ b/Overlay/Makefile
@@ -38,7 +38,7 @@ include $(DEVKITPRO)/libnx/switch_rules
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
APP_TITLE := ReverseNX-RT
-APP_VERSION := 1.1.1
+APP_VERSION := 1.1.1-OC
TARGET := ReverseNX-RT-ovl
BUILD := build
diff --git a/Overlay/source/main.cpp b/Overlay/source/main.cpp
index 810295c..2b3aa52 100644
--- a/Overlay/source/main.cpp
+++ b/Overlay/source/main.cpp
@@ -1,7 +1,202 @@
#define TESLA_INIT_IMPL // If you have more than one file using the tesla header, only define this in the main one
#include <tesla.hpp> // The Tesla Header
+#include <atomic>
#include "SaltyNX.h"
+class ModeSync {
+public:
+ enum ReverseNXMode {
+ ReverseNX_NotValid = 0,
+ ReverseNX_SystemDefault = 0,
+ ReverseNX_Handheld,
+ ReverseNX_Docked,
+ ReverseNX_Undefined,
+ };
+
+ void SetMode(bool isDefault, bool setDock, char* descBuf, size_t bufSize) {
+ auto changeHandler = [this](ReverseNXMode newMode) {
+ if (this->currentMode == ReverseNX_Undefined || this->currentMode != newMode) {
+ if (R_FAILED(this->ipc->SetMode(newMode))) {
+ this->ipc->ipcStatus = Ipc::IpcStatus_ConnectFailed;
+ return;
+ }
+ this->currentMode = newMode;
+ }
+ };
+
+ tsl::hlp::ScopeGuard updateBufGuard([&] { this->ipc->UpdateStatusDesc(descBuf, bufSize); });
+
+ if (isDefault) {
+ changeHandler(ReverseNX_SystemDefault);
+ return;
+ }
+
+ changeHandler(setDock ? ReverseNX_Docked : ReverseNX_Handheld);
+ }
+
+ ModeSync() {
+ this->ipc = new Ipc;
+ this->ipc->Init();
+ }
+
+ ~ModeSync() {
+ this->ipc->Exit();
+ delete this->ipc;
+ }
+
+protected:
+ struct Ipc {
+ #define API_VER 2
+ #define SERVICE_NAME "sysclkOC"
+
+ enum SysClkIpcCmd {
+ SysClkIpcCmd_GetApiVersion = 0,
+ SysClkIpcCmd_GetConfigValues = 9,
+ SysClkIpcCmd_SetReverseNXRTMode = 11,
+ };
+
+ enum SysClkConfigValue {
+ SysClkConfigValue_SyncReverseNXMode = 4,
+ SysClkConfigValue_EnumMax = 8,
+ };
+
+ struct SysClkConfigValueList {
+ uint64_t values[SysClkConfigValue_EnumMax];
+ };
+
+ enum IpcStatus {
+ IpcStatus_OK,
+
+ IpcStatus_Unknown,
+ IpcStatus_NotRunning,
+ IpcStatus_InitFailed,
+ IpcStatus_ConnectFailed,
+ IpcStatus_UnsupportedVer,
+
+ IpcStatus_Count,
+ };
+
+ static constexpr const char* IpcStatusStr[IpcStatus_Count] = {
+ "",
+
+ "Unknown",
+ "Err: Not running",
+ "Err: Failed to init",
+ "Err: Failed to connect",
+ "Err: Unsupported version",
+ };
+
+ Result Init() {
+ Result rc = 0;
+
+ rc = IpcInitialize();
+
+ rc = GetStatus();
+
+ return rc;
+ }
+
+ void Exit() {
+ if (--refCnt == 0)
+ serviceClose(&service);
+ }
+
+ bool IsServiceRunning() {
+ Handle handle;
+ SmServiceName name = smEncodeName(SERVICE_NAME);
+ if (R_FAILED(smRegisterService(&handle, name, false, 1))) {
+ return true;
+ }
+ svcCloseHandle(handle);
+ smUnregisterService(name);
+ return false;
+ }
+
+ Result IpcInitialize(void) {
+ Result rc = 0;
+ refCnt++;
+
+ if (serviceIsActive(&service))
+ return 0;
+
+ rc = smGetService(&service, SERVICE_NAME);
+
+ if (R_FAILED(rc)) {
+ this->ipcStatus = IpcStatus_InitFailed;
+ rc = this->ipcStatus;
+ this->Exit();
+ return rc;
+ }
+
+ return rc;
+ }
+
+ Result GetApiVersion(u32* outVer) {
+ return serviceDispatchOut(&service, SysClkIpcCmd_GetApiVersion, *outVer);
+ }
+
+ Result GetConfigValues(SysClkConfigValueList* outConfigValues) {
+ return serviceDispatchOut(&service, SysClkIpcCmd_GetConfigValues, *outConfigValues);
+ }
+
+ Result SetMode(ReverseNXMode mode) {
+ return serviceDispatchIn(&service, SysClkIpcCmd_SetReverseNXRTMode, mode);
+ }
+
+ Result GetStatus() {
+ if (!IsServiceRunning()) {
+ this->ipcStatus = IpcStatus_NotRunning;
+ return this->ipcStatus;
+ }
+
+ tsl::hlp::ScopeGuard exitSrvGuard([&] { this->Exit(); });
+
+ uint32_t api_ver;
+ if (R_FAILED(GetApiVersion(&api_ver))) {
+ this->ipcStatus = IpcStatus_ConnectFailed;
+ return this->ipcStatus;
+ }
+
+ if (api_ver != API_VER) {
+ this->ipcStatus = IpcStatus_UnsupportedVer;
+ return this->ipcStatus;
+ }
+
+ SysClkConfigValueList* list = new SysClkConfigValueList;
+ tsl::hlp::ScopeGuard listGuard([&] { delete list; });
+
+ if (R_FAILED(GetConfigValues(list))) {
+ this->ipcStatus = IpcStatus_ConnectFailed;
+ return this->ipcStatus;
+ }
+
+ exitSrvGuard.dismiss();
+
+ shouldSync = bool(list->values[SysClkConfigValue_SyncReverseNXMode]);
+ this->ipcStatus = IpcStatus_OK;
+ return this->ipcStatus;
+ }
+
+ void UpdateStatusDesc(char* buffer, size_t size) {
+ snprintf(buffer, size,
+ "Mode Sync: %s%s",
+ IpcStatusStr[ipcStatus],
+ (ipcStatus == IpcStatus_OK) ? (
+ shouldSync ? "ON" : "OFF"
+ ) : ""
+ );
+ }
+
+ Service service = {};
+ std::atomic<std::size_t> refCnt = 0;
+ IpcStatus ipcStatus = IpcStatus_Unknown;
+ bool shouldSync = false;
+ };
+
+ Ipc* ipc = nullptr;
+ ReverseNXMode currentMode = ReverseNX_Undefined;
+};
+
bool* def = 0;
bool* isDocked = 0;
bool* pluginActive = 0;
@@ -17,6 +212,7 @@ bool plugin = false;
char DockedChar[32];
char SystemChar[32];
char PluginChar[36];
+char SysclkChar[0x40];
uint64_t PID = 0;
Handle remoteSharedMemory = 1;
SharedMemory _sharedmemory = {};
@@ -73,7 +269,7 @@ bool CheckPort () {
class GuiTest : public tsl::Gui {
public:
- GuiTest(u8 arg1, u8 arg2, bool arg3) { }
+ GuiTest(ModeSync* p) : modeSync(p) { }
// Called when this Gui gets loaded to create the UI
// Allocate all elements on the heap. libtesla will make sure to clean them up when not needed anymore
@@ -112,6 +308,7 @@ public:
else {
renderer->drawString(SystemChar, false, x, y+40, 20, renderer->a(0xFFFF));
renderer->drawString(DockedChar, false, x, y+60, 20, renderer->a(0xFFFF));
+ renderer->drawString(SysclkChar, false, x, y+80, 20, renderer->a(0xFFFF));
}
}
}), 100);
@@ -190,6 +387,8 @@ public:
if (_def) sprintf(SystemChar, "Controlled by system: Yes");
else sprintf(SystemChar, "Controlled by system: No");
+
+ modeSync->SetMode(_def, _isDocked, SysclkChar, sizeof(SysclkChar));
}
else i++;
}
@@ -200,6 +399,8 @@ public:
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
return false; // Return true here to singal the inputs have been consumed
}
+
+ ModeSync* modeSync;
};
class OverlayTest : public tsl::Overlay {
@@ -248,9 +449,11 @@ public:
});
+ modeSync = new ModeSync;
} // Called at the start to initialize all services necessary for this Overlay
virtual void exitServices() override {
+ delete modeSync;
shmemClose(&_sharedmemory);
fsdevUnmountDevice("sdmc");
} // Callet at the end to clean up all services previously initialized
@@ -260,8 +463,10 @@ public:
virtual void onHide() override {} // Called before overlay wants to change from visible to invisible state
virtual std::unique_ptr<tsl::Gui> loadInitialGui() override {
- return initially<GuiTest>(1, 2, true); // Initial Gui to load. It's possible to pass arguments to it's constructor like this
+ return initially<GuiTest>(modeSync); // Initial Gui to load. It's possible to pass arguments to it's constructor like this
}
+
+ ModeSync* modeSync = nullptr;
};
int main(int argc, char **argv) {

View File

@@ -1,241 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include <unistd.h>
#include <switch.h>
/* Recompile nx-hbloader with following added in config.json "kernel_capabilities"
{
"type": "map",
"value": {
"address": "0x60006000",
"size": "0x1000",
"is_ro": false,
"is_io": true
}
}
*/
void waitForKey(PadState pad) {
while (appletMainLoop())
{
padUpdate(&pad);
u64 kDown = padGetButtonsDown(&pad);
if (kDown & HidNpadButton_A)
break;
if (kDown & HidNpadButton_Plus || kDown & HidNpadButton_B) {
consoleExit(NULL);
exit(0);
}
consoleUpdate(NULL);
}
}
#define CLK_RST_IO_BASE 0x60006000
#define CLK_RST_IO_SIZE 0x1000
#define REG(OFFSET) (*reinterpret_cast<volatile u32 *>(OFFSET))
#define GET_BITS(VAL, HIGH, LOW) ((VAL & ((1UL << (HIGH + 1UL)) - 1UL)) >> LOW)
#define GET_BIT(VAL, BIT) GET_BITS(VAL, BIT, BIT)
static u64 clkrst_base = 0;
static u64 clkrst_size = 0;
// From jetson nano kernel
typedef enum {
/* divider = 2 */
CLK_PLLX = 5,
CLK_PLLM = 2,
CLK_PLLMB = 37,
/* PLLX & PLLG are backup PLLs for CPU & GPU */
/* divider = 1 */
CLK_CCLK_G = 18, // A57 CPU cluster
CLK_EMC = 36,
} PTO_ID; // PLL Test Output Register ID
/* See if GM20B clock GPC PLL regs are accessible. */
#define PLLX_MISC0 0xE4
#define PLLM_MISC2 0x9C
double ptoGetMHz(PTO_ID pto_id, u32 divider = 1, u32 presel_reg = 0, u32 presel_mask = 0) {
u32 pre_val, val, presel_val;
if (presel_reg) {
val = REG(clkrst_base + presel_reg);
usleep(10);
presel_val = val & presel_mask;
val &= ~presel_mask;
val |= presel_mask;
REG(clkrst_base + presel_reg) = val;
usleep(10);
}
constexpr u32 cycle_count = 16;
pre_val = REG(clkrst_base + 0x60);
val = BIT(23) | BIT(13) | (cycle_count - 1);
val |= pto_id << 14;
REG(clkrst_base + 0x60) = val;
usleep(10);
REG(clkrst_base + 0x60) = val | BIT(10);
usleep(10);
REG(clkrst_base + 0x60) = val;
usleep(10);
REG(clkrst_base + 0x60) = val | BIT(9);
usleep(500);
while(REG(clkrst_base + 0x64) & BIT(31))
;
val = REG(clkrst_base + 0x64);
val &= 0xFFFFFF;
val *= divider;
double rate_mhz = (u64)val * 32768. / cycle_count / 1000. / 1000.;
usleep(10);
REG(clkrst_base + 0x60) = pre_val;
usleep(10);
if (presel_reg) {
val = REG(clkrst_base + presel_reg);
usleep(10);
val &= ~presel_mask;
val |= presel_val;
REG(clkrst_base + presel_reg) = val;
usleep(10);
}
return rate_mhz;
}
int main() {
consoleInit(NULL);
PadState pad;
padConfigureInput(1, HidNpadStyleSet_NpadStandard);
padInitializeDefault(&pad);
// Get clkrst MMIO virtual address
Result rc = svcQueryIoMapping(&clkrst_base, &clkrst_size, CLK_RST_IO_BASE, CLK_RST_IO_SIZE);
if (R_FAILED(rc)) {
printf("[ERROR] svcQueryIoMapping: 0x%X\n", rc);
consoleUpdate(NULL);
waitForKey(pad);
consoleExit(NULL);
}
printf("clkrst_base: 0x%lX\nclkrst_size: 0x%lX\n", clkrst_base, clkrst_size);
{
#define CLKRST_PLLX_BASE 0xE0
u32 pllx_base = REG(clkrst_base + CLKRST_PLLX_BASE);
printf("\n"\
"PLLX_BASE: 0x%X\n"\
"PLLX_ENABLE: %lu\n"\
"PLLX_REF_DIS: %lu\n"\
"PLLX_LOCK: %lu\n"\
"PLLX_DIVP: %lu\n"\
"PLLX_DIVN: %lu\n"\
"PLLX_DIVM: %lu\n",
pllx_base,
GET_BIT(pllx_base, 30),
GET_BIT(pllx_base, 29),
GET_BIT(pllx_base, 27),
GET_BITS(pllx_base, 24, 20),
GET_BITS(pllx_base, 15, 8),
GET_BITS(pllx_base, 7, 0));
}
{
#define CLKRST_PLLX_MISC 0xE4
u32 pllx_misc = REG(clkrst_base + CLKRST_PLLX_MISC);
printf("\n"\
"PLLX_MISC: 0x%X\n"\
"PLLX_FO_G_DISABLE: %lu\n"\
"PLLX_PTS: %lu\n"\
"PLLX_LOCK_ENABLE: %lu\n",
pllx_misc,
GET_BIT(pllx_misc, 28),
GET_BITS(pllx_misc, 23, 22),
GET_BIT(pllx_misc, 18));
}
{
#define CLKRST_PLLMB_SS_CFG 0x780
u32 pllmb_ss_cfg = REG(clkrst_base + CLKRST_PLLMB_SS_CFG);
printf("\n"\
"PLLMB_SS_CFG: 0x%X\n"\
"PLLMB_EN_SDM: %lu\n"\
"PLLMB_EN_SSC: %lu\n",
pllmb_ss_cfg,
GET_BIT(pllmb_ss_cfg, 31),
GET_BIT(pllmb_ss_cfg, 30));
}
{
#define CLKRST_PLLMB_SS_CTRL1 0x784
u32 pllmb_ss_ctrl1 = REG(clkrst_base + CLKRST_PLLMB_SS_CTRL1);
printf("\n"\
"PLLMB_SS_CTRL1: 0x%X\n"\
"PLLMB_SDM_SSC_MAX: %lu\n"\
"PLLMB_SDM_SSC_MIN: %lu\n",
pllmb_ss_ctrl1,
GET_BITS(pllmb_ss_ctrl1, 31, 16),
GET_BITS(pllmb_ss_ctrl1, 15, 0));
}
{
#define CLKRST_PLLM_BASE 0x90
u32 pllm_base = REG(clkrst_base + CLKRST_PLLM_BASE);
printf("\n"\
"PLLM_BASE: 0x%X\n"\
"PLLM_DIVP: %lu\n"\
"PLLM_DIVN: %lu\n"\
"PLLM_DIVM: %lu\n",
pllm_base,
GET_BITS(pllm_base, 24, 20),
GET_BITS(pllm_base, 15, 8),
GET_BITS(pllm_base, 7, 0));
}
{
#define CLKRST_PLLMB_BASE 0x5e8
u32 pllmb_base = REG(clkrst_base + CLKRST_PLLMB_BASE);
printf("\n"\
"PLLMB_BASE: 0x%X\n"\
"PLLMB_DIVP: %lu\n"\
"PLLMB_DIVN: %lu\n"\
"PLLMB_DIVM: %lu\n",
pllmb_base,
GET_BITS(pllmb_base, 24, 20),
GET_BITS(pllmb_base, 15, 8),
GET_BITS(pllmb_base, 7, 0));
}
printf("\n"\
"EMC: %6.1f MHz\n"\
"CCLK_G: %6.1f MHz\n"\
"PLLX: %6.1f MHz\n"\
"PLLM: %6.1f MHz\n"\
"PLLMB: %6.1f MHz\n",
ptoGetMHz(CLK_EMC),
ptoGetMHz(CLK_CCLK_G),
ptoGetMHz(CLK_PLLX, 2, PLLX_MISC0, BIT(22)),
ptoGetMHz(CLK_PLLM, 2, PLLM_MISC2, BIT(8)),
ptoGetMHz(CLK_PLLMB, 2, PLLM_MISC2, BIT(9))
);
waitForKey(pad);
consoleExit(NULL);
return 0;
}

View 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)"

View 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];
}
}

View 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

View 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);
}

View 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

View 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 = .;
}
}

View 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();
}

View 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

View 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
View 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

Binary file not shown.

53
Source/hoc-clk/bitmap.py Normal file
View File

@@ -0,0 +1,53 @@
from PIL import Image
import argparse
import os
def image_to_rgba8888_array(image_path, output_path):
# Open and convert to RGBA
img = Image.open(image_path).convert('RGBA')
width, height = img.size
# Get pixel data
pixels = img.tobytes()
# Write as C header file
with open(output_path, 'w') as f:
f.write('// This is a automatically generated file, do not edit manually.\n')
f.write(f'// {os.path.basename(image_path)} - {width}x{height}\n')
f.write(f'const unsigned int IMG_WIDTH = {width};\n')
f.write(f'const unsigned int IMG_HEIGHT = {height};\n')
f.write('const unsigned char IMG_DATA[] = {\n ')
for i, byte in enumerate(pixels):
f.write(f'0x{byte:02X}')
if i < len(pixels) - 1:
f.write(', ')
if (i + 1) % 12 == 0:
f.write('\n ')
f.write('\n};\n')
print(f'Converted: {width}x{height} -> {len(pixels)} bytes')
print(f'Output: {output_path}')
def main():
parser = argparse.ArgumentParser(
description='PNG to RGB8888 script'
)
parser.add_argument('input', help='Input image file (e.g. cat.png)')
parser.add_argument(
'-o', '--output',
help='Output header file (default: <input>.h)'
)
args = parser.parse_args()
output_path = args.output
if not output_path:
base, _ = os.path.splitext(args.input)
output_path = base + '.h'
image_to_rgba8888_array(args.input, output_path)
if __name__ == '__main__':
main()

24
Source/sys-clk/build.sh → Source/hoc-clk/build.sh Normal file → Executable file
View File

@@ -9,30 +9,36 @@ if [[ -n "$1" ]]; then
DIST_DIR="$1"
fi
echo "DIST_DIR: $DIST_DIR"
echo "CORES: $CORES"
echo "*** sysmodule ***"
TITLE_ID="$(grep -oP '"title_id":\s*"0x\K(\w+)' "$ROOT_DIR/sysmodule/perms.json")"
echo
echo "*** Compiling hoc-clk ***"
TITLE_ID="$(sed -n 's/.*"title_id"[[:space:]]*:[[:space:]]*"0x\([^"]*\)".*/\1/p' "$ROOT_DIR/sysmodule/perms.json")"
pushd "$ROOT_DIR/sysmodule"
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
echo "*** Copying lang ***"
cp -vr "$ROOT_DIR/overlay/lang/" "$DIST_DIR/config/horizon-oc/lang/"
echo

View 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;
}

View File

@@ -1,3 +1,20 @@
/*
* Copyright (c) meha3945 (hanai3bi)
*
* 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 <utility>

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -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>
@@ -27,27 +27,28 @@
#pragma once
#include <cstdint>
#include <switch.h> // include libnx
#ifdef __cplusplus
#include "cpp_util.hpp"
extern "C" {
#endif
typedef std::uint32_t Result;
typedef std::uint32_t u32;
typedef std::int32_t s32;
typedef std::uint64_t u64;
typedef std::int64_t s64;
typedef std::uint8_t u8;
typedef std::int16_t s16;
typedef std::uint16_t u16;
// typedef std::uint32_t Result;
// typedef std::uint32_t u32;
// typedef std::int32_t s32;
// typedef std::uint64_t u64;
// typedef std::int64_t s64;
// typedef std::uint8_t u8;
// 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
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -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[];

View 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";
// }
// }

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -32,29 +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 sysclkIpcSetReverseNXRTMode(ReverseNXMode mode);
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();
Result hocClkIpcUpdateEmcRegs();
Result hocClkIpcCalculateGpuVmin();
static inline Result sysclkIpcRemoveOverride(SysClkModule module)
static inline Result hocclkIpcRemoveOverride(HocClkModule module)
{
return sysclkIpcSetOverride(module, 0);
return hocclkIpcSetOverride(module, 0);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,

View 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);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -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>
@@ -29,23 +29,19 @@
#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,
HocClkConfigValue_EristaMaxCpuClock,
HocClkConfigValue_EristaMaxGpuClock,
HocClkConfigValue_EristaMaxMemClock,
HocClkConfigValue_MarikoMaxCpuClock,
HocClkConfigValue_MarikoMaxGpuClock,
HocClkConfigValue_MarikoMaxMemClock,
HocClkConfigValue_ThermalThrottle,
HocClkConfigValue_ThermalThrottleThreshold,
@@ -55,26 +51,45 @@ typedef enum {
HocClkConfigValue_LiteTDPLimit,
HocClkConfigValue_EnforceBoardLimit,
HocClkConfigValue_KipEditing,
HocClkConfigValue_KipFileName,
HocClkConfigValue_BatteryChargeCurrent,
HorizonOCConfigValue_BatteryChargeCurrent,
HocClkConfigValue_OverwriteRefreshRate,
HocClkConfigValue_MaxDisplayClockH,
HorizonOCConfigValue_OverwriteRefreshRate,
HocClkConfigValue_DVFSMode,
HocClkConfigValue_DVFSOffset,
HocClkConfigValue_LiveCpuUv,
HocClkConfigValue_EnableExperimentalSettings,
HocClkConfigValue_FixCpuVoltBug,
HocClkConfigValue_GPUScheduling,
HocClkConfigValue_GPUSchedulingMethod,
HocClkConfigValue_RAMVoltDisplayMode,
HocClkConfigValue_CpuGovernorMinimumFreq,
HocClkConfigValue_DisplayVoltage,
HocClkConfigValue_MemoryFrequencyMeasurementMode,
HocClkConfigValue_RamDisplayUnit,
HocClkConfigValue_IsFirstLoad,
HocClkConfigValue_AulaDisplayColorPreset,
HocClkConfigValue_MarikoMiddleFreqs,
KipConfigValue_custRev,
KipConfigValue_mtcConf,
KipConfigValue_KipVersion,
// KipConfigValue_mtcConf,
KipConfigValue_hpMode,
KipConfigValue_commonEmcMemVolt,
KipConfigValue_eristaEmcMaxClock,
KipConfigValue_eristaEmcMaxClock1,
KipConfigValue_eristaEmcMaxClock2,
KipConfigValue_stepMode,
KipConfigValue_marikoEmcMaxClock,
KipConfigValue_marikoEmcVddqVolt,
KipConfigValue_emcDvbShift,
KipConfigValue_marikoSocVmax,
KipConfigValue_t1_tRCD,
KipConfigValue_t2_tRP,
@@ -84,6 +99,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,
@@ -107,6 +139,7 @@ typedef enum {
KipConfigValue_marikoGpuUV,
KipConfigValue_marikoGpuVmin,
KipConfigValue_marikoGpuBootVolt,
KipConfigValue_marikoGpuVmax,
KipConfigValue_commonGpuVoltOffset,
@@ -165,26 +198,30 @@ typedef enum {
KipConfigValue_g_volt_e_1036800,
KipConfigValue_g_volt_e_1075200,
SysClkConfigValue_EnumMax,
} SysClkConfigValue;
KipConfigValue_t6_tRTW_fine_tune,
KipConfigValue_t7_tWTR_fine_tune,
KipCrc32,
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:
@@ -193,18 +230,10 @@ 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";
case HocClkConfigValue_EristaMaxGpuClock:
return pretty ? "Max GPU Clock" : "gpu_max_e";
case HocClkConfigValue_EristaMaxMemClock:
return pretty ? "Erista Max MEM Clock" : "mem_max_e";
return pretty ? "CPU Max Clock" : "cpu_max_e";
case HocClkConfigValue_MarikoMaxCpuClock:
return pretty ? "CPU Max Display Clock" : "cpu_max_m";
case HocClkConfigValue_MarikoMaxGpuClock:
return pretty ? "Mariko Max GPU Clock" : "gpu_max_m";
case HocClkConfigValue_MarikoMaxMemClock:
return pretty ? "Mariko Max MEM Clock" : "mem_max_m";
case HocClkConfigValue_ThermalThrottle:
return pretty ? "Thermal Throttle" : "thermal_throttle";
@@ -221,29 +250,57 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
case HocClkConfigValue_LiteTDPLimit:
return pretty ? "Handheld TDP Limit" : "tdp_limit_l";
case HocClkConfigValue_EnforceBoardLimit:
return pretty ? "Enforce Board Limit" : "enforce_board_limit";
case HocClkConfigValue_KipEditing:
return pretty ? "Enable KIP Editing" : "kip_editing";
case HocClkConfigValue_KipFileName:
return pretty ? "KIP File Name" : "kip_file_name";
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 HocClkConfigValue_FixCpuVoltBug:
return pretty ? "Fix CPU Volt Bug" : "cpu_volt_bugfix";
case HocClkConfigValue_MaxDisplayClockH:
return pretty ? "Max Display Clock (Handheld)" : "drr_max_clock";
case HocClkConfigValue_DVFSMode:
return pretty ? "DVFS Mode" : "dvfs_mode";
case HocClkConfigValue_DVFSOffset:
return pretty ? "DVFS Offset" : "dvfs_offset";
case HocClkConfigValue_GPUScheduling:
return pretty ? "GPU Scheduling" : "gpu_scheduling";
case HocClkConfigValue_GPUSchedulingMethod:
return pretty ? "GPU Scheduling Method" : "gpu_sched_method";
case HocClkConfigValue_LiveCpuUv:
return pretty ? "Live CPU Undervolt" : "live_cpu_uv";
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";
case KipConfigValue_mtcConf:
return pretty ? "MTC Config" : "kip_mtc_conf";
case KipConfigValue_KipVersion:
return pretty ? "KIP Version" : "kip_version";
// case KipConfigValue_mtcConf:
// return pretty ? "MTC Config" : "kip_mtc_conf";
case KipConfigValue_hpMode:
return pretty ? "HP Mode" : "kip_hp_mode";
@@ -251,14 +308,21 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
case KipConfigValue_commonEmcMemVolt:
return pretty ? "Common EMC/MEM Voltage" : "common_emc_mem_volt";
case KipConfigValue_eristaEmcMaxClock:
return pretty ? "Erista EMC Max Clock" : "erista_emc_max_clock";
return pretty ? "Erista EMC Max Clock 1" : "erista_emc_max_clock";
case KipConfigValue_eristaEmcMaxClock1:
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";
@@ -276,6 +340,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:
@@ -324,6 +417,8 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
return pretty ? "Mariko GPU Undervolt" : "mariko_gpu_uv";
case KipConfigValue_marikoGpuVmin:
return pretty ? "Mariko GPU Vmin" : "mariko_gpu_vmin";
case KipConfigValue_marikoGpuBootVolt:
return pretty ? "Mariko GPU Boot Voltage" : "mariko_gpu_boot_volt";
case KipConfigValue_marikoGpuVmax:
return pretty ? "Mariko GPU Vmax" : "mariko_gpu_vmax";
@@ -386,98 +481,110 @@ 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_tune";
case KipConfigValue_t7_tWTR_fine_tune: return pretty ? "t7 - tWTR Fine Tune" : "t7_tWTR_fine_tune";
case KipCrc32:
return pretty ? "CRC32" : "crc32";
case HocClkConfigValue_IsFirstLoad:
return pretty ? "Is First Load" : "is_first_load";
default:
return pretty ? "[cfg] no enum format string" : "err_no_format_string";
}
}
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 HocClkConfigValue_KipFileName:
case HorizonOCConfigValue_BatteryChargeCurrent:
case HorizonOCConfigValue_OverwriteRefreshRate:
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;
case HocClkConfigValue_EristaMaxGpuClock:
return 921ULL;
case HocClkConfigValue_EristaMaxMemClock:
return 1600ULL;
case HocClkConfigValue_MarikoMaxCpuClock:
return 1963ULL;
case HocClkConfigValue_MarikoMaxGpuClock:
return 1075ULL;
case HocClkConfigValue_MarikoMaxMemClock:
return 1862ULL;
case HocClkConfigValue_ThermalThrottle:
case HocClkConfigValue_HandheldTDP:
case HocClkConfigValue_EnforceBoardLimit:
case HocClkConfigValue_KipEditing:
case HocClkConfigValue_FixCpuVoltBug:
case HocClkConfigValue_IsFirstLoad:
case HocClkConfigValue_DVFSMode:
return 1ULL;
case HocClkConfigValue_ThermalThrottleThreshold:
return 70ULL;
case HocClkConfigValue_HandheldTDPLimit:
return 8600ULL;
return 9600ULL; // 8600mW will trigger on erista stock, so raise it a bit
case HocClkConfigValue_LiteTDPLimit:
return 6400ULL;
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)
{
case HocClkConfigValue_EristaMaxCpuClock:
case HocClkConfigValue_EristaMaxGpuClock:
case HocClkConfigValue_EristaMaxMemClock:
case HocClkConfigValue_MarikoMaxCpuClock:
case HocClkConfigValue_MarikoMaxGpuClock:
case HocClkConfigValue_MarikoMaxMemClock:
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 HocClkConfigValue_EnforceBoardLimit:
case HocClkConfigValue_KipEditing:
case HocClkConfigValue_KipFileName:
case HorizonOCConfigValue_OverwriteRefreshRate:
case HocClkConfigValue_FixCpuVoltBug:
case HocClkConfigValue_OverwriteRefreshRate:
case HocClkConfigValue_IsFirstLoad:
case HocClkConfigValue_EnableExperimentalSettings:
case HocClkConfigValue_LiveCpuUv:
case HocClkConfigValue_GPUSchedulingMethod:
case HocClkConfigValue_MarikoMiddleFreqs:
return (input & 0x1) == input;
case KipConfigValue_KipVersion:
case KipConfigValue_custRev:
case KipConfigValue_mtcConf:
// case KipConfigValue_mtcConf:
case KipConfigValue_hpMode:
case KipConfigValue_commonEmcMemVolt:
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:
@@ -486,6 +593,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:
@@ -503,6 +622,7 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case KipConfigValue_eristaGpuVmin:
case KipConfigValue_marikoGpuUV:
case KipConfigValue_marikoGpuVmin:
case KipConfigValue_marikoGpuBootVolt:
case KipConfigValue_marikoGpuVmax:
case KipConfigValue_commonGpuVoltOffset:
case KipConfigValue_gpuSpeedo:
@@ -559,10 +679,24 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case KipConfigValue_g_volt_e_1075200:
case KipConfigValue_eristaCpuVmin:
case KipConfigValue_eristaCpuUnlock:
case KipConfigValue_t6_tRTW_fine_tune:
case KipConfigValue_t7_tWTR_fine_tune:
case KipCrc32:
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;
return true;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -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_SocThermFail = 3,
} SysClkError;
HocClkError_Generic = 0,
HocClkError_ConfigNotLoaded = 1,
HocClkError_ConfigSaveFailed = 2,
// HocClkError_SocThermFail = 3,
} HocClkError;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) Souldbminer and Horizon OC Contributors
* 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,
@@ -31,45 +31,42 @@
#include "board.h"
#include "clock_manager.h"
#define SYSCLK_IPC_API_VERSION 4
#define SYSCLK_IPC_SERVICE_NAME "sys: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,
SysClkIpcCmd_SetReverseNXRTMode = 12,
HocClkIpcCmd_SetKipData = 13,
HocClkIpcCmd_GetKipData = 14,
HocClkIpcCmd_UpdateEmcRegs = 15,
HocClkIpcCmd_CalculateGpuVmin = 16,
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,
};
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;

View 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. */

View 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
}
///@}

View 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);
}

View File

@@ -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.29
APP_VERSION := 2.4.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this
TARGET_VERSION := $(APP_VERSION)
#---------------------------------------------------------------------------------
@@ -49,13 +49,14 @@ 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
UI_OVERRIDE_PATH := /config/sys-clk/
export MSYS2_ARG_CONV_EXCL := -DUI_OVERRIDE_PATH
UI_OVERRIDE_PATH := /config/horizon-oc/
CFLAGS += -DUI_OVERRIDE_PATH="\"$(UI_OVERRIDE_PATH)\""
# Disable fstream
@@ -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)

View 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"
}

View 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 "
}

View 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 "
}

Some files were not shown because too many files have changed in this diff Show More