Compare commits

..
98 Commits
Author SHA1 Message Date
niklascfw 5d8ca16e22 overlay: remove the in-app Updates page 2026-10-16 23:05:22 +02:00
niklascfw 71407da80e Merge upstream Horizon-OC 2.6.0 changelog. 2026-10-16 22:00:48 +02:00
souldbminerr 301c5f675f Create CHANGELOG.md 2026-10-15 20:38:01 -04:00
niklascfw 8a930448eb Point Atmosphere clones at Atmosphere-Pro boot-storage.
Use the OmniNX host for the exosphere patches, and add build-exosphere.sh so exosphere can be built on its own.
2026-10-10 17:49:18 +02:00
Lightos1 7960dd9be6 benchmark-toolbox: make cpu readings *actually* match memtoolkit 2026-10-03 17:01:49 +02:00
Lightos1 a71c88942d hoc-clk: fix config low t8 typo 2026-09-29 20:18:08 +02:00
Lightos1 603f9d7ca2 ldr_process_creation: actually update to latest 2026-09-28 20:44:13 +02:00
souldbminersmwc b0d111ad17 update configurator for 2805 max clock 2026-09-28 19:39:41 +02:00
Lightos1 cd7c211cdf ldr_process_creation: update to latest 2026-09-28 12:29:44 +02:00
Lightos1 3714aecf32 revert bpmp related exosphere patches 2026-09-28 12:23:32 +02:00
souldbminersmwc 2a10aa7cc5 Update misc_gui.cpp 2026-09-27 19:45:20 -04:00
souldbminersmwc 47bdc26a1a ready for ams 1.12.0 2026-09-27 19:32:00 -04:00
Lightos1 192fc41f1a bump version 2026-09-14 14:26:57 +02:00
Lightos1 73eae055c0 add benchmark toolbox fix to 2.6.0 2026-09-11 22:34:36 +02:00
souldbminersmwc 044d7b69e0 2805 cpu 2026-09-10 19:23:30 -04:00
souldbminersmwc 59ea651360 jedec step default 2026-09-10 19:17:48 -04:00
souldbminersmwc 12597f8aa3 DRAMID from MRR 2026-09-10 19:17:08 -04:00
souldbminersmwc 4143857358 add gpu registers 2026-09-10 19:14:49 -04:00
souldbminersmwc 28e8ca37e1 Mariko MEM voltage reduce max 2026-09-10 19:13:44 -04:00
souldbminersmwc 52424a86e3 update changelog 2026-09-10 19:09:58 -04:00
souldbminersmwc 56495e9b64 gm20b driver bugfix 2026-09-10 19:05:30 -04:00
souldbminersmwc 151d8584e4 update exosphere patch 2026-09-10 19:04:20 -04:00
Lightos1 b9bf6dd719 remove erista mrf repointing 2026-07-27 14:59:09 +02:00
Lightos1 0befcd2d75 use hooking for erista mrf 2026-07-27 14:58:14 +02:00
Lightos1 3d5dbf77ea pcv_erista: hook nn::pcv::clkrst::GetEristaMtcTable 2026-07-26 20:21:20 +02:00
Lightos1 ab1fde543d ldr: add hooking capabilities 2026-07-26 20:16:00 +02:00
Lightos1 66cfbe0d0b clkmgr: don't apply gpudvfs during GameStartMemWar if gpu dvfs is disabled 2026-07-26 13:45:59 +02:00
Lightos1 d92aaff7c1 fix missing { 2026-07-23 21:02:13 +02:00
Lightos1 dac56d58b5 bump versions 2026-07-23 20:52:52 +02:00
Lightos1 987e37f4ab Revert "HOC 3.0.0 part 1"
This reverts commit 5bb89bfc54.
2026-07-23 20:38:16 +02:00
Lightos1 73dbd98d5b Revert "Update customize.cpp"
This reverts commit 81dca160e3.
2026-07-23 20:33:39 +02:00
Lightos1 30371125c6 ldr: fix formating 2026-07-23 20:31:19 +02:00
Lightos1 555a18739d hoc-clk: fix freq bug 2026-07-23 20:20:23 +02:00
souldbminersmwc 81dca160e3 Update customize.cpp 2026-07-22 21:48:13 -04:00
souldbminersmwc 5bb89bfc54 HOC 3.0.0 part 1
- version change to athena (3.0.0)
- cust rev 7
- build script change
- EMC 64LUT
2026-07-22 20:20:57 -04:00
Lightos1 d9906a794c horizon-oc-monitor (deprecated): add note 2026-07-18 20:02:04 +02:00
Lightos1 954ca103d2 deprecate horizon-oc-monitor 2026-07-18 19:58:01 +02:00
Lightos1 fc7f8c0820 Merge pull request #103 from Niklas080208/main
Add JSON language support for sysmodule notifications
2026-07-18 18:55:18 +02:00
Lightos1 5d0494cb55 Merge branch 'main' into main 2026-07-18 18:55:01 +02:00
Niklas080208 6c4b356b0c Use PascalCase for lang helpers and constexpr names. 2026-07-18 18:49:19 +02:00
Lightos1 679f81df36 overlay: more speedo range stuff 2026-07-18 16:07:40 +02:00
Lightos1 60f5de0890 overlay: **actually** extend ram freq to 3400 2026-07-18 15:30:06 +02:00
Lightos1 7084ed2bc4 overlay: extend ram freq to 3400 2026-07-18 15:26:36 +02:00
Lightos1 932ec4bc02 updated chinese translations by xinluochenjiang 2026-07-18 15:18:09 +02:00
Lightos1 e16aed8db4 update build.sh to copy hekate payload 2026-07-18 01:29:36 +02:00
Niklas080208 230c71927a Add JSON language support for sysmodule notifications.
Load Ultrahand locale translations from /config/horizon-oc/lang so user-facing hoc-clk alerts match the overlay language.
2026-07-16 00:49:39 +02:00
Lightos1 d950186b30 Update submodules 2026-07-15 20:50:13 +02:00
Lightos1 587325cccf overlay: remove unwinding stacks to save ram 2026-07-15 19:30:23 +02:00
Lightos1 b2d2025688 remove t2 cap, add minor timing tweaks 2026-07-15 19:12:12 +02:00
Lightos1 0de76f1616 Erista timing fixes 2026-07-13 23:07:08 +02:00
Lightos1 8d4fcd14e0 Bump cust revision 2026-07-12 20:34:56 +02:00
Lightos1 60cffadf8e add timing fixes to erista 2026-07-12 20:00:20 +02:00
Lightos1 6309950402 extend soc volt table 2026-07-12 16:24:36 +02:00
Lightos1 ee3ac5c38a erista cpuVmin should be 825mV by default 2026-07-12 15:44:12 +02:00
Lightos1 4c52b1418d pcv_mariko: Add (partial) mc_emem_arb_misc0 calculation 2026-07-12 15:40:35 +02:00
Lightos1 e6178da315 Add more labeling to configurator 2026-07-09 21:44:42 +02:00
Lightos1 c8ccc95715 ram boost war: fix crash during boot 2026-07-09 18:42:54 +02:00
Lightos1 1bb088f251 rename gpuHz variables 2026-07-09 18:28:19 +02:00
Lightos1 ce8ec3cc60 fix ram reset during game start up 2026-07-09 18:25:43 +02:00
Lightos1 7d01e40e33 full swapto transition in overlay 2026-07-09 16:31:25 +02:00
Lightos1 54ae13439b fix stupid comment typo 2026-07-08 20:25:37 +02:00
Lightos1 896ae8b014 pcv_mariko: timing fixes
dram_timings struct uses tRP and tRFC stock values in ns.
nWR in emc_mrw needed to be adjusted.
2026-07-08 20:24:08 +02:00
Souldbminer ac4d4b3bf9 Update README.md 2026-07-05 20:04:47 -04:00
Souldbminer b30dd05bb0 Update README.md 2026-07-05 20:04:32 -04:00
Lightos1 96bab62e3c Update build.sh 2026-07-05 23:12:37 +02:00
Lightos1 2f5f3b99ac fix typo in readme 2026-07-05 18:39:24 +02:00
Lightos1 752a37ed23 change changeTo to swapTo 2026-07-05 15:52:11 +02:00
Lightos1 9d6a551455 update build.sh to use latest tag 2026-07-04 22:39:24 +02:00
Lightos1 539d1949a8 add warning when setting disabled freq 2026-07-04 20:33:19 +02:00
Lightos1 00d3e1cf82 bump version 2026-07-04 20:21:30 +02:00
Lightos1 572b88afaf on the fly gpu table ui stuff 2026-07-04 20:15:14 +02:00
Lightos1 7766481ff6 add on the fly gpu volt table 2026-07-04 18:18:51 +02:00
Lightos1 cb0ffbcd29 add gpu voltage request 2026-07-04 13:44:45 +02:00
Lightos1 d7df43e962 fix eristaGpuDvfsTableSLT 2026-07-03 08:02:17 +02:00
Lightos1 1bde55256f raise HiOPT15 max and allow >1267 on it via custom table 2026-07-03 07:20:15 +02:00
Lightos1 220e9f91f5 Update README.md 2026-06-29 16:29:15 +02:00
Lightos1 f7e75b1807 Raise default gpu high uv limits to 1305
From the knowledge gained from ina tests and math, the pmic limits have
been busted. Raising the limits to 1305 (max supported by dvfs tables)
is quite reasonable. Up to 1420MHz gpu is theoretically below pmic
limits even on terrible binning. CPU limits remaing unchanged due to
voltage concerns.
2026-06-29 15:43:50 +02:00
Lightos1 1f4a0f5310 update build.sh 2026-06-29 15:29:50 +02:00
Lightos1 3542ee8f52 dist folder itself must be present 2026-06-29 15:21:02 +02:00
Lightos1 d11eefe7ca temporarily allow dist again 2026-06-29 15:18:19 +02:00
Lightos1 5332e839f5 add dist to .gitignore 2026-06-29 15:12:04 +02:00
Lightos1 09ea28468e remove dist 2026-06-29 15:11:11 +02:00
Lightos1 859b225258 remove extra space 2026-06-27 19:48:13 +02:00
Lightos1 f3886c60b0 add kip detection 2026-06-27 19:47:00 +02:00
souldbminersmwc cd9d494de9 hocclk: fix auto cpu ram oc 2026-06-26 20:24:48 -04:00
Souldbminer d80d59d894 Merge pull request #99 from XiaoYeeMoon/main
Update zh-cn translation
2026-06-22 17:32:21 -04:00
souldbminersmwc 5a90c2d060 lower t2 cap
t2=2 crashes on some rams like mgcj
2026-06-22 12:46:07 -04:00
Lightos1 bbaa8eb36d hoc-clk overlay: built with Os to save memory 2026-06-22 15:45:52 +02:00
XiaoYee Moon a6f202fc80 Add translation for RAM Latency Editor 2026-06-22 10:43:37 +08:00
XiaoYee Moon 5fab6da7b8 Correct & add zh-cn translation 2026-06-22 10:29:22 +08:00
XiaoYee Moon d1a9698659 Correct zh-cn translation 2026-06-22 08:47:47 +08:00
Lightos1 83e4dde1ed build.sh: avoid additional lang folder 2026-06-21 20:05:17 +02:00
Souldbminer 236ee7100a Merge pull request #96 from XiaoYeeMoon/main
Update zh-cn translation
2026-06-19 18:13:11 -04:00
XiaoYee Moon 5b9b003ade Add files via upload 2026-06-20 05:52:50 +08:00
XiaoYee Moon 665b7cb601 Update zh-cn translation 2026-06-20 04:18:37 +08:00
Souldbminer 3676412a4e Merge pull request #94 from redraz/patch-2
Update Russian translation
2026-06-18 13:11:18 -04:00
redraz 87bde7d1fc Update Russian translation
Corrected/added new lines
2026-06-18 20:09:53 +03:00
Lightos1 2a3256d4af fix aligmnent 2026-06-17 11:53:26 +02:00
193 changed files with 10913 additions and 6948 deletions

No files matched your search

+2
View File
@@ -6,3 +6,5 @@ __pycache__/
build
*.dksh
Source/hekate
dist/*
!dist/.gitignore
-8
View File
@@ -1,11 +1,3 @@
[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
+10
View File
@@ -0,0 +1,10 @@
# Horizon OC 2.6.0
- loader:
- Update for Atmosphere 1.12.1
- Add 2805MHz CPU and 1235mV VMAX (use with caution!)
- Make JEDEC step mode default
- hoc-clk:
- Fix bugs in GM20B driver
- hocate:
- update to 6.5.4
**Full Changelog**: https://github.com/Horizon-OC/Horizon-OC/compare/3.0.0-test1...2.6.0-rel
+12 -5
View File
@@ -21,7 +21,7 @@
> **THIS TOOL CAN BE DANGEROUS IF MISUSED. PROCEED WITH CAUTION.**
> Due to the design of Horizon OS, **overclocking RAM can cause NAND OR SD CORRUPTION.**
> Ensure you have a **full NAND, PROINFO, EMUMMC and SD backup** before proceeding.
> Ensure you have a **full NAND, PRODINFO, EMUMMC and SD backup** before proceeding.
---
@@ -72,6 +72,12 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
---
## Donating
We don't accept donations. Put your spare cash into a charity instead :)
---
## Building from Source
Refer to COMPILATION.md
@@ -137,7 +143,8 @@ Refer to COMPILATION.md
* 665
### CPU clocks (mhz)
* 2703 → mariko absolute max, dangerous
* 2805 → mariko absolute max, dangerous
* 2703
* 2601 → unsafe
* 2499
* 2397 → mariko safe max with UV (low speedo)
@@ -160,9 +167,9 @@ Refer to COMPILATION.md
* 612 → sleep mode
### GPU clocks (mhz)
* 1536 → absolute max clock on mariko. very dangerous
* 1459
* 1382
* 1536 → absolute max clock on mariko. Dangerous
* 1497 → max mariko unsafe clock
* 1382 → Max mariko "safe" clock
* 1305
* 1267 → NVIDIA T214(mariko) rating
* 1228 → mariko High UV safe clock
+1
View File
@@ -0,0 +1 @@
This contains a diff all modifications done to exosphere
@@ -0,0 +1,57 @@
131,156c131,159
< #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(FuseKFuse, MemoryController, 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 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(ExternalMemoryController0, MemoryController1, UINT64_C(0x7001E000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
> HANDLER(ExternalMemoryController1, ExternalMemoryController0, UINT64_C(0x7001F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
> HANDLER(Sdmmc, ExternalMemoryController1, 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__) \
@@ -0,0 +1,29 @@
1221c1221
< const auto target_fw = GetTargetFirmware();
---
> // const auto target_fw = GetTargetFirmware();
1223,1226c1223,1226
< if (target_fw >= TargetFirmware_2_0_0) {
< /* Set the PMC secure. */
< reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, ENABLE));
< }
---
> // if (target_fw >= TargetFirmware_2_0_0) {
> // /* Set the PMC secure. */ I REFUSE
> // reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, ENABLE));
> // }
1228,1233c1228,1234
< if (target_fw >= TargetFirmware_4_0_0) {
< /* Set the MC secure. */
< reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, SLAVE_SECURITY_REG_BITS_ENUM(1, MC0, ENABLE),
< SLAVE_SECURITY_REG_BITS_ENUM(1, MC1, ENABLE),
< SLAVE_SECURITY_REG_BITS_ENUM(1, MCB, ENABLE));
< }
---
> // if (target_fw >= TargetFirmware_4_0_0) {
> // /* Set the MC secure. */ I REFUSE
> // reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG1_0, SLAVE_SECURITY_REG_BITS_ENUM(1, MC0, ENABLE),
> // SLAVE_SECURITY_REG_BITS_ENUM(1, MC1, ENABLE),
> // SLAVE_SECURITY_REG_BITS_ENUM(1, MCB, ENABLE));
> // }
> return;
@@ -0,0 +1,2 @@
154a155
> { 0xF0000005, Restriction_None, SmcShowError },
@@ -0,0 +1,9 @@
160c160
< /* Validate that the bpmp is appropriately halted. */
---
> /* Horizon-OC: Halt BPMP here as hoc-clk may be running it, and if it's running there will be a panic. */
161a162,165
> reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
> FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED));
>
> /* Validate that the bpmp is appropriately halted. */
@@ -0,0 +1,22 @@
100a101,105
> #include "secmon_define_emc_access_table.inc"
> #include "secmon_define_emc01_access_table.inc"
> #include "secmon_define_flow_controller_access_table.inc"
> #include "secmon_define_exception_vectors_access_table.inc"
> #include "secmon_define_rtc_pmc_access_table.inc"
104,107c109,118
< { PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, },
< { McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::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, },
> { Emc01AccessTable::ReducedAccessTable.data(), Emc01AccessTable::Address + MemoryRegionVirtualDeviceExternalMemoryController0.GetAddress(), Emc01AccessTable::Address + MemoryRegionPhysicalDeviceExternalMemoryController0.GetAddress(), Emc01AccessTable::Size, },
> { Emc01AccessTable::ReducedAccessTable.data(), Emc01AccessTable::Address + MemoryRegionVirtualDeviceExternalMemoryController1.GetAddress(), Emc01AccessTable::Address + MemoryRegionPhysicalDeviceExternalMemoryController1.GetAddress(), Emc01AccessTable::Size, },
> { FlowControllerAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceFlowController.GetAddress(), FlowControllerAccessTable::Address, FlowControllerAccessTable::Size, },
> { ExceptionVectorsAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExceptionVectors.GetAddress(), ExceptionVectorsAccessTable::Address, ExceptionVectorsAccessTable::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, },
@@ -0,0 +1,25 @@
/*
* 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/>.
*/
#define __ACCESS_TABLE_NAME__ Emc01AccessTable
#define __ACCESS_TABLE_ADDRESS__ 0
#define __ACCESS_TABLE_INC__ "secmon_emc01_access_table_data.inc"
#include "secmon_define_access_table.inc"
#undef __ACCESS_TABLE_INC__
#undef __ACCESS_TABLE_ADDRESS__
#undef __ACCESS_TABLE_NAME__
File diff suppressed because it is too large. Load diff
@@ -151,7 +151,9 @@ namespace ams::secmon {
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(ExternalMemoryController0, MemoryController1, UINT64_C(0x7001E000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
HANDLER(ExternalMemoryController1, ExternalMemoryController0, UINT64_C(0x7001F000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
HANDLER(Sdmmc, ExternalMemoryController1, 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__) \
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,571 @@
/*
* 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_cache.hpp"
#include "../secmon_cpu_context.hpp"
#include "../secmon_error.hpp"
#include "../secmon_misc.hpp"
#include "secmon_smc_power_management.hpp"
#include "secmon_smc_se_lock.hpp"
#include "sc7fw_bin.h"
namespace ams::secmon {
/* Declare assembly functionality. */
void *GetCoreExceptionStackVirtual();
}
namespace ams::secmon::smc {
/* Declare assembly power-management functionality. */
void PivotStackAndInvoke(void *stack, void (*function)());
void FinalizePowerOff();
namespace {
constexpr inline const uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress();
constexpr inline const uintptr_t APB_MISC = MemoryRegionVirtualDeviceApbMisc.GetAddress();
constexpr inline const uintptr_t GPIO = MemoryRegionVirtualDeviceGpio.GetAddress();
constexpr inline const uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress();
constexpr inline const uintptr_t EVP = secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress();
constexpr inline const uintptr_t FLOW_CTLR = MemoryRegionVirtualDeviceFlowController.GetAddress();
constexpr inline const uintptr_t AHB_ARBC = MemoryRegionVirtualDeviceSystem.GetAddress();
constexpr inline uintptr_t CommonSmcStackTop = MemoryRegionVirtualTzramVolatileData.GetEndAddress() - (0x80 * (NumCores - 1));
enum PowerStateType {
PowerStateType_StandBy = 0,
PowerStateType_PowerDown = 1,
};
enum PowerStateId {
PowerStateId_Sc7 = 27,
};
/* http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf Page 46 */
struct SuspendCpuPowerState {
using StateId = util::BitPack32::Field< 0, 16, PowerStateId>;
using StateType = util::BitPack32::Field<16, 1, PowerStateType>;
using PowerLevel = util::BitPack32::Field<24, 2, u32>;
};
constinit bool g_charger_hi_z_mode_enabled = false;
constinit const reg::BitsMask CpuPowerGateStatusMasks[NumCores] = {
PMC_REG_BITS_MASK(PWRGATE_STATUS_CE0),
PMC_REG_BITS_MASK(PWRGATE_STATUS_CE1),
PMC_REG_BITS_MASK(PWRGATE_STATUS_CE2),
PMC_REG_BITS_MASK(PWRGATE_STATUS_CE3),
};
constinit const APBDEV_PMC_PWRGATE_TOGGLE_PARTID CpuPowerGateTogglePartitionIds[NumCores] = {
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2,
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3,
};
bool IsCpuPoweredOn(const reg::BitsMask mask) {
return reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, REG_BITS_VALUE_FROM_MASK(mask, APBDEV_PMC_PWRGATE_STATUS_STATUS_ON));
}
void PowerOnCpu(const reg::BitsMask mask, u32 toggle_partid) {
/* If the cpu is already on, we have nothing to do. */
if (IsCpuPoweredOn(mask)) {
return;
}
/* Wait until nothing is being powergated. */
int timeout = 5000;
while (true) {
if (reg::HasValue(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) {
break;
}
util::WaitMicroSeconds(1);
if ((--timeout) < 0) {
/* NOTE: Nintendo doesn't do any error handling here... */
return;
}
}
/* Toggle on the cpu partition. */
reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE),
PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, toggle_partid));
/* Wait up to 5000 us for the powergate to complete. */
timeout = 5000;
while (true) {
if (IsCpuPoweredOn(mask)) {
break;
}
util::WaitMicroSeconds(1);
if ((--timeout) < 0) {
/* NOTE: Nintendo doesn't do any error handling here... */
return;
}
}
}
void ResetCpu(int which_core) {
reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */
REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */
}
void StartCpu(int which_core) {
reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */
REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */
}
void PowerOffCpuImpl() {
/* Get the current core id. */
const auto core_id = hw::GetCurrentCoreId();
/* Configure the flow controller to prepare for shutting down the current core. */
flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_ONLY);
flow::SetHaltCpuEvents(core_id, false);
flow::SetCc4Ctrl(core_id, 0);
/* Save the core's context for restoration on next power-on. */
SaveDebugRegisters();
SetCoreOff();
/* Ensure there are no pending memory transactions prior to our power-down. */
FlushEntireDataCache();
/* Finalize our powerdown and wait for an interrupt. */
FinalizePowerOff();
}
void ValidateSocStateForSuspend() {
/* Validate that all other cores are off. */
AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0)));
/* Horizon-OC: Halt BPMP here as hoc-clk may be running it, and if it's running there will be a panic. */
const bool jtag = IsJtagEnabled();
reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED));
/* Validate that the bpmp is appropriately halted. */
AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED)));
/* Further validations aren't guaranteed on < 6.0.0. */
if (GetTargetFirmware() < TargetFirmware_6_0_0) {
return;
}
/* Validate that USB2, APB-DMA, AHB-DMA are held in reset. */
AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_REG_BITS_ENUM(RST_DEVICES_H_SWR_USB2_RST, ENABLE),
CLK_RST_REG_BITS_ENUM(RST_DEVICES_H_SWR_APBDMA_RST, ENABLE),
CLK_RST_REG_BITS_ENUM(RST_DEVICES_H_SWR_AHBDMA_RST, ENABLE)));
/* Validate that USBD is held in reset. */
AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_USBD_RST, ENABLE)));
/* Validate that AHB-DMA, USB, USB2, COP are not allowed to arbitrate on the AHB. */
AMS_ABORT_UNLESS(reg::HasValue(AHB_ARBC + AHB_ARBITRATION_DISABLE, AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_COP, DISABLE),
AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_AHBDMA, DISABLE),
AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_USB, DISABLE),
AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_USB2, DISABLE)));
/* Validate that the GPIO controller has clock enabled. */
AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_GPIO, ENABLE)));
/* Validate that both FUSE and KFUSE have clock enabled. */
AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_H_CLK_ENB_FUSE, ENABLE),
CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_H_CLK_ENB_KFUSE, ENABLE)));
/* Validate that all of IRAM has clock enabled. */
AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_IRAMA, ENABLE),
CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_IRAMB, ENABLE),
CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_IRAMC, ENABLE),
CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_IRAMD, ENABLE)));
/* Validate that ACTMON has clock enabled. */
AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_V_CLK_ENB_ACTMON, ENABLE)));
/* Validate that ENTROPY has clock enabled. */
AMS_ABORT_UNLESS(reg::HasValue(CLK_RST + CLK_RST_CONTROLLER_CLK_OUT_ENB_W, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_W_CLK_ENB_ENTROPY, ENABLE)));
}
void GenerateCryptographicallyRandomBytes(void * const dst, int size) {
/* Flush the region we're about to fill to ensure consistency with the SE. */
hw::FlushDataCache(dst, size);
hw::DataSynchronizationBarrierInnerShareable();
/* Generate random bytes. */
se::GenerateRandomBytes(dst, size);
hw::DataSynchronizationBarrierInnerShareable();
/* Flush to ensure the CPU sees consistent data for the region. */
hw::FlushDataCache(dst, size);
hw::DataSynchronizationBarrierInnerShareable();
}
void SaveSecureContextForErista() {
/* Generate a random key source. */
util::AlignedBuffer<hw::DataCacheLineSize, se::AesBlockSize> key_source;
GenerateCryptographicallyRandomBytes(key_source, se::AesBlockSize);
const u32 * const key_source_32 = reinterpret_cast<const u32 *>(static_cast<u8 *>(key_source));
/* Ensure that the key source registers are not locked. */
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) != pmc::LockState::Locked);
/* Write the key source, lock writes to the key source, and verify that the key source is write-locked. */
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, key_source_32[0]);
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, key_source_32[1]);
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, key_source_32[2]);
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, key_source_32[3]);
pmc::LockSecureRegister(pmc::SecureRegister_KeySourceWrite);
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceWrite) == pmc::LockState::Locked);
/* Verify the key source is correct in registers, and read-lock the key source registers. */
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24) == key_source_32[0]);
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25) == key_source_32[1]);
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26) == key_source_32[2]);
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27) == key_source_32[3]);
pmc::LockSecureRegister(pmc::SecureRegister_KeySourceRead);
/* Ensure that the key source registers are locked. */
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) == pmc::LockState::Locked);
/* Generate a random kek into keyslot 2. */
se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek);
/* Verify that the se is in a validate state, context save, and validate again. */
{
se::ValidateErrStatus();
ON_SCOPE_EXIT { se::ValidateErrStatus(); };
{
/* Transition to non-secure mode for the duration of the context save operation. */
se::SetSecure(false);
ON_SCOPE_EXIT { se::SetSecure(true); };
/* Get a pointer to the context storage. */
se::Context * const context = MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetPointer<se::Context>();
static_assert(MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetSize() == sizeof(*context));
/* Save the context. */
se::SaveContext(context);
/* Ensure that the cpu sees consistent data. */
hw::FlushDataCache(context, sizeof(*context));
hw::DataSynchronizationBarrierInnerShareable();
/* Write the context pointer to pmc scratch, so that the bootrom will restore it on wake. */
reg::Write(PMC + APBDEV_PMC_SCRATCH43, MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState.GetAddress());
}
}
/* Clear keyslot 3, and then derive the save key. */
se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey);
se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, se::AesBlockSize);
/* Declare a temporary block to be used as both iv and mac. */
u32 temp_block[se::AesBlockSize / sizeof(u32)] = {};
/* Ensure that the SE sees consistent data for tzram. */
const void * const tzram_save_src = MemoryRegionVirtualTzramReadOnlyAlias.GetPointer<u8>() + MemoryRegionVirtualTzramVolatileData.GetSize() + MemoryRegionVirtualTzramVolatileStack.GetSize();
void * const tzram_save_dst = MemoryRegionVirtualIramSc7Work.GetPointer<void>();
constexpr size_t TzramSaveSize = MemoryRegionVirtualDramSecureDataStoreTzram.GetSize();
hw::FlushDataCache(tzram_save_src, TzramSaveSize);
hw::FlushDataCache(tzram_save_dst, TzramSaveSize);
hw::DataSynchronizationBarrierInnerShareable();
/* Encrypt tzram using our random key. */
se::EncryptAes256Cbc(tzram_save_dst, TzramSaveSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize, temp_block, se::AesBlockSize);
hw::FlushDataCache(tzram_save_dst, TzramSaveSize);
hw::DataSynchronizationBarrierInnerShareable();
/* Copy the data from work space to the secure storage destination. */
void * const tzram_store_dst = MemoryRegionVirtualDramSecureDataStoreTzram.GetPointer<void>();
std::memcpy(tzram_store_dst, tzram_save_dst, TzramSaveSize);
hw::FlushDataCache(tzram_store_dst, TzramSaveSize);
hw::DataSynchronizationBarrierInnerShareable();
/* Compute cmac of tzram into our temporary block. */
se::ComputeAes256Cmac(temp_block, se::AesBlockSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize);
/* Ensure that the cmac registers are not locked. */
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) != pmc::LockState::Locked);
/* Write the cmac, lock writes to the cmac, and verify that the cmac is write-locked. */
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, temp_block[0]);
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, temp_block[1]);
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, temp_block[2]);
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, temp_block[3]);
pmc::LockSecureRegister(pmc::SecureRegister_CmacWrite);
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacWrite) == pmc::LockState::Locked);
/* Verify the key source is correct in registers, and read-lock the key source registers. */
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112) == temp_block[0]);
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113) == temp_block[1]);
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114) == temp_block[2]);
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115) == temp_block[3]);
pmc::LockSecureRegister(pmc::SecureRegister_CmacRead);
/* Ensure that the key source registers are locked. */
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) == pmc::LockState::Locked);
}
void SaveSecureContextForMariko() {
/* Save security engine context to TZRAM SE carveout (inaccessible to cpu). */
se::SaveContextAutomatic();
/* Save TZRAM to shadow-TZRAM in always-on power domain. */
se::SaveTzramAutomatic();
}
void SaveSecureContext() {
/* Save the appropriate secure context. */
const auto soc_type = GetSocType();
if (soc_type == fuse::SocType_Erista) {
SaveSecureContextForErista();
} else /* if (soc_type == fuse::SocType_Mariko) */ {
SaveSecureContextForMariko();
}
/* Save the debug code. */
#if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)
{
const void * const debug_code_src = MemoryRegionVirtualDebugCode.GetPointer<void>();
void * const debug_code_dst = MemoryRegionVirtualDramDebugDataStore.GetPointer<void>();
std::memcpy(debug_code_dst, debug_code_src, MemoryRegionVirtualDebugCode.GetSize());
hw::FlushDataCache(debug_code_dst, MemoryRegionVirtualDebugCode.GetSize());
hw::DataSynchronizationBarrierInnerShareable();
}
#endif
}
void LoadAndStartSc7BpmpFirmware() {
/* Set BPMP reset. */
reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_COP_RST, ENABLE));
/* Set the PMC as insecure, so that the BPMP firmware can access it. */
reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, DISABLE));
/* Set the exception vectors for the bpmp. RESET should point to RESET, all others should point to generic exception/panic. */
constexpr const u32 Sc7FirmwareResetVector = static_cast<u32>(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x0);
constexpr const u32 Sc7FirmwarePanicVector = static_cast<u32>(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x4);
reg::Write(EVP + EVP_COP_RESET_VECTOR, Sc7FirmwareResetVector);
reg::Write(EVP + EVP_COP_UNDEF_VECTOR, Sc7FirmwarePanicVector);
reg::Write(EVP + EVP_COP_SWI_VECTOR, Sc7FirmwarePanicVector);
reg::Write(EVP + EVP_COP_PREFETCH_ABORT_VECTOR, Sc7FirmwarePanicVector);
reg::Write(EVP + EVP_COP_DATA_ABORT_VECTOR, Sc7FirmwarePanicVector);
reg::Write(EVP + EVP_COP_RSVD_VECTOR, Sc7FirmwarePanicVector);
reg::Write(EVP + EVP_COP_IRQ_VECTOR, Sc7FirmwarePanicVector);
reg::Write(EVP + EVP_COP_FIQ_VECTOR, Sc7FirmwarePanicVector);
/* Disable activity monitor bpmp monitoring, so that we don't panic upon bpmp wake. */
actmon::StopMonitoringBpmp();
/* Load the bpmp firmware. */
void * const sc7fw_load_address = MemoryRegionVirtualIramSc7Firmware.GetPointer<void>();
std::memcpy(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size);
hw::FlushDataCache(sc7fw_load_address, sc7fw_bin_size);
hw::DataSynchronizationBarrierInnerShareable();
/* Ensure that the bpmp firmware was loaded. */
AMS_ABORT_UNLESS(crypto::IsSameBytes(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size));
/* Clear BPMP reset. */
reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_COP_RST, ENABLE));
/* Start the bpmp. */
reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_NONE));
}
void SaveSecureContextAndSuspend() {
/* Ensure there are no pending memory transactions before we continue */
FlushEntireDataCache();
hw::DataSynchronizationBarrierInnerShareable();
/* Save all secure context (security engine state + tzram). */
SaveSecureContext();
/* Load and start the sc7 firmware on the bpmp. */
if (GetTargetFirmware() >= TargetFirmware_2_0_0) {
LoadAndStartSc7BpmpFirmware();
}
/* Log our suspension. */
/* NOTE: Nintendo only does this on dev, but we will always do it. */
if (true /* !pkg1::IsProduction() */) {
log::Initialize(secmon::GetLogPort(), secmon::GetLogBaudRate(), secmon::GetLogFlags());
log::SendText("OYASUMI\n", 8);
log::Flush();
}
/* If we're on erista, configure the bootrom to allow our custom warmboot firmware. */
if (GetSocType() == fuse::SocType_Erista) {
reg::Write(PMC + APBDEV_PMC_SCRATCH31, 0x2202E012);
reg::Write(PMC + APBDEV_PMC_SCRATCH32, 0x6001DC28);
}
/* Finalize our powerdown and wait for an interrupt. */
FinalizePowerOff();
}
SmcResult SuspendCpuImpl(SmcArguments &args) {
/* Decode arguments. */
const util::BitPack32 power_state = { static_cast<u32>(args.r[1]) };
const uintptr_t entry_point = args.r[2];
const uintptr_t context_id = args.r[3];
const auto state_type = power_state.Get<SuspendCpuPowerState::StateType>();
const auto state_id = power_state.Get<SuspendCpuPowerState::StateId>();
const auto core_id = hw::GetCurrentCoreId();
/* Validate arguments. */
SMC_R_UNLESS(state_type == PowerStateType_PowerDown, PsciDenied);
SMC_R_UNLESS(state_id == PowerStateId_Sc7, PsciDenied);
/* Orchestrate charger transition to Hi-Z mode if needed. */
if (IsChargerHiZModeEnabled()) {
/* Ensure we can do comms over i2c-1. */
clkrst::EnableI2c1Clock();
/* If the charger isn't in hi-z mode, perform a transition. */
if (!charger::IsHiZMode()) {
charger::EnterHiZMode();
/* Wait up to 50ms for the transition to complete. */
const auto start_time = util::GetMicroSeconds();
auto current_time = start_time;
while ((current_time - start_time) <= 50'000) {
if (auto intr_status = reg::Read(GPIO + 0x634); (intr_status & 1) == 0) {
/* Wait 256 us to ensure the transition completes. */
util::WaitMicroSeconds(256);
break;
}
current_time = util::GetMicroSeconds();
}
}
/* Disable i2c-1, since we're done communicating over it. */
clkrst::DisableI2c1Clock();
}
/* Enable wake event detection. */
pmc::EnableWakeEventDetection();
/* Ensure that i2c-5 is usable for communicating with the pmic. */
clkrst::EnableI2c5Clock();
i2c::Initialize(i2c::Port_5);
/* Orchestrate sleep entry with the pmic. */
pmic::EnableSleep();
/* Ensure that the soc is in a state valid for us to suspend. */
if (GetTargetFirmware() >= TargetFirmware_2_0_0) {
ValidateSocStateForSuspend();
}
/* Configure the pmc for sc7 entry. */
pmc::ConfigureForSc7Entry();
/* Configure the flow controller for sc7 entry. */
flow::SetCc4Ctrl(core_id, 0);
flow::SetHaltCpuEvents(core_id, false);
flow::ClearL2FlushControl();
flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_TURNOFF_CPURAIL);
/* Save the entry context. */
SetEntryContext(core_id, entry_point, context_id);
/* Configure the cpu context for reset. */
SaveDebugRegisters();
SetCoreOff();
SetResetExpected(true);
/* Switch to use the common smc stack (all other cores are off), and perform suspension. */
PivotStackAndInvoke(reinterpret_cast<void *>(CommonSmcStackTop), SaveSecureContextAndSuspend);
/* This code will never be reached. */
__builtin_unreachable();
}
}
void PowerOffCpu() {
/* Get the current core id. */
const auto core_id = hw::GetCurrentCoreId();
/* Note that we're expecting a reset for the current core. */
SetResetExpected(true);
/* If we're on the final core, shut down directly. Otherwise, invoke with special stack. */
if (core_id == NumCores - 1) {
PowerOffCpuImpl();
} else {
PivotStackAndInvoke(GetCoreExceptionStackVirtual(), PowerOffCpuImpl);
}
/* This code will never be reached. */
__builtin_unreachable();
}
SmcResult SmcPowerOffCpu(SmcArguments &args) {
AMS_UNUSED(args);
PowerOffCpu();
}
SmcResult SmcPowerOnCpu(SmcArguments &args) {
/* Get and validate the core to power on. */
const int which_core = args.r[1];
SMC_R_UNLESS(0 <= which_core && which_core < NumCores, PsciInvalidParameters);
/* Ensure the core isn't already on. */
SMC_R_UNLESS(!IsCoreOn(which_core), PsciAlreadyOn);
/* Save the entry context. */
SetEntryContext(which_core, args.r[2], args.r[3]);
/* Reset the cpu. */
ResetCpu(which_core);
/* Turn on the core. */
PowerOnCpu(CpuPowerGateStatusMasks[which_core], CpuPowerGateTogglePartitionIds[which_core]);
/* Start the core. */
StartCpu(which_core);
return SmcResult::PsciSuccess;
}
SmcResult SmcSuspendCpu(SmcArguments &args) {
return LockSecurityEngineAndInvoke(args, SuspendCpuImpl);
}
bool IsChargerHiZModeEnabled() {
return g_charger_hi_z_mode_enabled;
}
void SetChargerHiZModeEnabled(bool en) {
g_charger_hi_z_mode_enabled = en;
}
}
@@ -99,16 +99,19 @@ 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_emc01_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, },
{ RtcPmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceRtcPmc.GetAddress(), RtcPmcAccessTable::Address, RtcPmcAccessTable::Size, },
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, },
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, },
{ PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, },
{ McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, },
{ EmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController.GetAddress(), EmcAccessTable::Address, EmcAccessTable::Size, },
{ Emc01AccessTable::ReducedAccessTable.data(), Emc01AccessTable::Address + MemoryRegionVirtualDeviceExternalMemoryController0.GetAddress(), Emc01AccessTable::Address + MemoryRegionPhysicalDeviceExternalMemoryController0.GetAddress(), Emc01AccessTable::Size, },
{ Emc01AccessTable::ReducedAccessTable.data(), Emc01AccessTable::Address + MemoryRegionVirtualDeviceExternalMemoryController1.GetAddress(), Emc01AccessTable::Address + MemoryRegionPhysicalDeviceExternalMemoryController1.GetAddress(), Emc01AccessTable::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) {
@@ -170,12 +173,12 @@ namespace ams::secmon::smc {
/* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */
/* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */
constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress();
SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument);
// SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument);
/* For backwards compatibility, we'll allow access to these devices on 1.0.0. */
if (GetTargetFirmware() < TargetFirmware_2_0_0) {
// /* For backwards compatibility, we'll allow access to these devices on 1.0.0. */
// if (GetTargetFirmware() < TargetFirmware_2_0_0) {
virtual_address = MemoryRegionVirtualDeviceMemoryController.GetAddress() + (address - MC);
}
// }
}
/* Perform the read or write, if we should. */
@@ -126,8 +126,7 @@ namespace ams::ldr {
bool g_is_ptm;
/* Global Zstd decompression context. */
constexpr size_t ZstdDctxWorkspaceSize = 0x176E8;
alignas(8) u8 g_zstd_dctx_workspace[ZstdDctxWorkspaceSize];
alignas(8) u8 g_zstd_dctx_workspace[util::DecompressZstdWithBicWorkBufferSizeDefault];
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
/* No version verification is done before 8.1.0. */
@@ -181,8 +180,8 @@ namespace ams::ldr {
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);
Npdm::ProcessAddressSpace GetProcessAddressSpace(const Meta *meta) {
return static_cast<Npdm::ProcessAddressSpace>((meta->npdm->flags0 & Npdm::MetaFlag0_ProcessAddressSpaceMask) >> Npdm::MetaFlag0_ProcessAddressSpaceShift);
}
Acid::PoolPartition GetPoolPartition(const Meta *meta) {
@@ -225,7 +224,7 @@ namespace ams::ldr {
/* Read NSO header. */
size_t read_size;
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, g_nso_headers + ctx.nso_count, sizeof(NsoHeader)));
R_UNLESS(read_size == sizeof(NsoHeader), ldr::ResultInvalidNso());
R_UNLESS_LOG(read_size == sizeof(NsoHeader), ldr::ResultInvalidNso(), "[ldr] NSO header truncated!\n");
/* Note nso is present. */
switch (i) {
@@ -264,7 +263,7 @@ namespace ams::ldr {
Result CheckAutoLoad(const AutoLoadModuleContext &ctx, u32 acid_flags) {
/* We must always have a main. */
R_UNLESS(ctx.ali.has_main, ldr::ResultInvalidNso());
R_UNLESS_LOG(ctx.ali.has_main, ldr::ResultInvalidNso(), "[ldr] Missing main!\n");
/* Validate flags and extents for all present NSOs. */
for (int i = 0; i < ctx.nso_count; ++i) {
@@ -272,11 +271,11 @@ namespace ams::ldr {
/* All NSOs must not be --X. */
/* This is "probably" not checked on Ounce? */
R_UNLESS((hdr.flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
R_UNLESS_LOG((hdr.flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso(), "[ldr] NSO[%d] --x not allowed!\n", i);
/* 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());
R_UNLESS_LOG((hdr.flags & NsoHeader::Flag_UseZbicCompression) == 0, ldr::ResultInvalidNso(), "[ldr] NSO[%d] zbic not allowed!\n", i);
}
/* NSOs must have page-aligned segments. */
@@ -299,13 +298,13 @@ namespace ams::ldr {
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());
R_UNLESS_LOG(ctx.ali.has_rtld, ldr::ResultInvalidNso(), "[ldr] Missing rtld!\n");
/* If we have rtld, we must not have browser core dll. */
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
R_UNLESS_LOG(!has_browser_dll, ldr::ResultInvalidNso(), "[ldr] BrowserCoreDll must not be present!\n");
} else {
/* We must not have both subsdk and browser dll. */
R_UNLESS(!(ctx.ali.has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
R_UNLESS_LOG(!(ctx.ali.has_subsdk && has_browser_dll), ldr::ResultInvalidNso(), "[ldr] Can't have both subsdk and BrowserCoreDll!\n");
}
R_SUCCEED();
@@ -394,6 +393,8 @@ namespace ams::ldr {
/* If the signature check fails, we need to check if this is allowable. */
if (!is_signature_valid) {
AMS_LOG("[ldr] invalid signature!\n");
/* 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());
@@ -407,52 +408,56 @@ namespace ams::ldr {
R_SUCCEED();
}
Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
const u8 meta_flags = meta->npdm->flags;
Result GetCreateProcessParameterFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
const u8 meta_flags0 = meta->npdm->flags0;
const u8 meta_flags1 = meta->npdm->flags1;
u32 flags = 0;
/* Set Is64Bit. */
if (meta_flags & Npdm::MetaFlag_Is64Bit) {
flags |= svc::CreateProcessFlag_Is64Bit;
if (meta_flags0 & Npdm::MetaFlag0_Is64BitInstruction) {
flags |= svc::CreateProcessParameterFlag_64Bit;
}
/* Set AddressSpaceType. */
switch (GetAddressSpaceType(meta)) {
case Npdm::AddressSpaceType_32Bit:
flags |= svc::CreateProcessFlag_AddressSpace32Bit;
/* Set ProcessAddressSpace. */
switch (GetProcessAddressSpace(meta)) {
case Npdm::ProcessAddressSpace_32Bit:
flags |= svc::CreateProcessParameterFlag_AddressSpace32Bit;
break;
case Npdm::AddressSpaceType_64BitDeprecated:
flags |= svc::CreateProcessFlag_AddressSpace64BitDeprecated;
case Npdm::ProcessAddressSpace_64Bit36:
flags |= svc::CreateProcessParameterFlag_AddressSpace64Bit36;
break;
case Npdm::AddressSpaceType_32BitWithoutAlias:
flags |= svc::CreateProcessFlag_AddressSpace32BitWithoutAlias;
case Npdm::ProcessAddressSpace_32BitNoReserved:
flags |= svc::CreateProcessParameterFlag_AddressSpace32BitNoReserved;
break;
case Npdm::AddressSpaceType_64Bit:
flags |= svc::CreateProcessFlag_AddressSpace64Bit;
case Npdm::ProcessAddressSpace_64Bit39:
flags |= svc::CreateProcessParameterFlag_AddressSpace64Bit39;
break;
case Npdm::ProcessAddressSpace_64Bit42:
flags |= svc::CreateProcessParameterFlag_AddressSpace64Bit42;
break;
default:
R_THROW(ldr::ResultInvalidMeta());
}
/* Set Enable Debug. */
if (ldr_flags & CreateProcessFlag_EnableDebug) {
flags |= svc::CreateProcessFlag_EnableDebug;
if (ldr_flags & CreateProcessParameterFlag_EnableJitDebug) {
flags |= svc::CreateProcessParameterFlag_EnableJitDebug;
}
/* Set Enable ASLR. */
if (!(ldr_flags & CreateProcessFlag_DisableAslr)) {
flags |= svc::CreateProcessFlag_EnableAslr;
if (!(ldr_flags & CreateProcessParameterFlag_DisableAslr)) {
flags |= svc::CreateProcessParameterFlag_EnableAslr;
}
/* Set Is Application. */
if (IsApplication(meta)) {
flags |= svc::CreateProcessFlag_IsApplication;
flags |= svc::CreateProcessParameterFlag_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;
if (meta_flags0 & Npdm::MetaFlag0_OptimizeMemoryAllocation) {
flags |= svc::CreateProcessParameterFlag_OptimizeMemoryAllocation;
}
}
}
@@ -464,19 +469,19 @@ namespace ams::ldr {
switch (GetPoolPartition(meta)) {
case Acid::PoolPartition_Application:
if (IsApplet(meta)) {
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
flags |= svc::CreateProcessParameterFlag_PoolPartitionApplet;
} else {
flags |= svc::CreateProcessFlag_PoolPartitionApplication;
flags |= svc::CreateProcessParameterFlag_PoolPartitionApplication;
}
break;
case Acid::PoolPartition_Applet:
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
flags |= svc::CreateProcessParameterFlag_PoolPartitionApplet;
break;
case Acid::PoolPartition_System:
flags |= svc::CreateProcessFlag_PoolPartitionSystem;
flags |= svc::CreateProcessParameterFlag_PoolPartitionSystem;
break;
case Acid::PoolPartition_SystemNonSecure:
flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure;
flags |= svc::CreateProcessParameterFlag_PoolPartitionSystemNonSecure;
break;
default:
R_THROW(ldr::ResultInvalidMeta());
@@ -484,18 +489,23 @@ namespace ams::ldr {
} 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;
flags |= svc::CreateProcessParameterFlag_DeprecatedUseSecureMemory;
}
}
/* 11.0.0+/meso Set Disable DAS merge. */
if (meta_flags & Npdm::MetaFlag_DisableDeviceAddressSpaceMerge) {
flags |= svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge;
/* 11.0.0+/meso Set Disable DAS Merge. */
if (meta_flags0 & Npdm::MetaFlag0_DisableDeviceAddressSpaceMerge) {
flags |= svc::CreateProcessParameterFlag_DisableDeviceAddressSpaceMerge;
}
/* 18.0.0+/meso Set Alias region extra size. */
if (meta_flags & Npdm::MetaFlag_EnableAliasRegionExtraSize) {
flags |= svc::CreateProcessFlag_EnableAliasRegionExtraSize;
/* 18.0.0+/meso Set Enable Address Sanitizer . */
if (meta_flags0 & Npdm::MetaFlag0_EnableAddressSanitizer) {
flags |= svc::CreateProcessParameterFlag_EnableAddressSanitizer;
}
/* 23.0.0+/meso Set Enable Shadow Stack. */
if (meta_flags1 & Npdm::MetaFlag1_EnableShadowStack) {
flags |= svc::CreateProcessParameterFlag_EnableShadowStack;
}
*out = flags;
@@ -513,7 +523,7 @@ namespace ams::ldr {
out->reslimit = resource_limit;
/* Set flags. */
R_TRY(GetCreateProcessFlags(std::addressof(out->flags), meta, flags));
R_TRY(GetCreateProcessParameterFlags(std::addressof(out->flags), meta, flags));
/* 3.0.0+ System Resource Size. */
if (hos::GetVersion() >= hos::Version_3_0_0) {
@@ -523,7 +533,7 @@ namespace ams::ldr {
/* Validate system resource usage. */
if (meta->npdm->system_resource_size) {
/* Process must be 64-bit. */
R_UNLESS((out->flags & svc::CreateProcessFlag_AddressSpace64Bit), ldr::ResultInvalidMeta());
R_UNLESS((out->flags & svc::CreateProcessParameterFlag_AddressSpace64Bit39), ldr::ResultInvalidMeta());
/* Process must be application or applet. */
R_UNLESS(IsApplication(meta) || IsApplet(meta), ldr::ResultInvalidMeta());
@@ -567,6 +577,11 @@ namespace ams::ldr {
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);
/* Reserve hook arena memory past pcv's bss. */
if (g_is_pcv && i == ctx.main_nso_idx) {
out->nso_size[i] = util::AlignUp(out->nso_size[i], os::MemoryPageSize) + hoc::pcv::PcvDataArenaSize;
}
const size_t aligned_up_size = util::AlignUp(out->nso_size[i], os::MemoryPageSize) & (AutoLoadModuleSizeMax - 1);
R_UNLESS(out->nso_size[i] <= aligned_up_size, ldr::ResultInvalidNso());
R_UNLESS(aligned_up_size > 0, ldr::ResultInvalidNso());
@@ -591,25 +606,29 @@ namespace ams::ldr {
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:
switch (out_param->flags & svc::CreateProcessParameterFlag_AddressSpaceMask) {
case svc::CreateProcessParameterFlag_AddressSpace32Bit:
case svc::CreateProcessParameterFlag_AddressSpace32BitNoReserved:
aslr_start = svc::AddressSmallMap32Start;
aslr_size = svc::AddressSmallMap32Size;
break;
case svc::CreateProcessFlag_AddressSpace64BitDeprecated:
case svc::CreateProcessParameterFlag_AddressSpace64Bit36:
aslr_start = svc::AddressSmallMap36Start;
aslr_size = svc::AddressSmallMap36Size;
break;
case svc::CreateProcessFlag_AddressSpace64Bit:
case svc::CreateProcessParameterFlag_AddressSpace64Bit39:
aslr_start = svc::AddressMap39Start;
aslr_size = svc::AddressMap39Size;
break;
case svc::CreateProcessParameterFlag_AddressSpace64Bit42:
aslr_start = svc::AddressMap42Start;
aslr_size = svc::AddressMap42Size;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
} else {
/* On 1.0.0, only 2 address space types existed. */
if (out_param->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) {
if (out_param->flags & svc::CreateProcessParameterFlag_AddressSpace64Bit36) {
aslr_start = svc::AddressSmallMap36Start;
aslr_size = svc::AddressSmallMap36Size;
} else {
@@ -622,7 +641,7 @@ namespace ams::ldr {
/* Set Create Process output. */
uintptr_t aslr_slide = 0;
size_t free_size = (aslr_size - total_size);
if (out_param->flags & svc::CreateProcessFlag_EnableAslr) {
if (out_param->flags & svc::CreateProcessParameterFlag_EnableAslr) {
aslr_slide = GenerateSecureRandom(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize;
}
@@ -644,10 +663,13 @@ namespace ams::ldr {
R_SUCCEED();
}
Result LoadAutoLoadModuleSegment(fs::FileHandle file, size_t file_offset, size_t compressed_size, size_t segment_size, bool is_compressed, bool is_zstd, uintptr_t map_base, uintptr_t map_end) {
Result LoadAutoLoadModuleSegment(fs::FileHandle file, size_t file_offset, size_t compressed_size, size_t segment_size, bool is_compressed, bool is_zbic, uintptr_t map_base, uintptr_t map_end) {
/* Select read size based on compression. */
size_t file_size = is_compressed ? compressed_size : segment_size;
AMS_LOG("[ldr] Loading segment @ 0x%016lx: compressed=%d, file_size=0x%08lx, compressed_size=0x%08lx, segment_size=0x%08lx\n",
map_base, is_compressed, file_size, 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());
@@ -657,19 +679,19 @@ namespace ams::ldr {
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());
R_UNLESS_LOG(read_size == file_size, ldr::ResultInvalidNso(), "[ldr] Couldn't read segment from file!\n");
/* 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());
if (is_zbic) {
bool decompressed = util::DecompressZstdWithBic(reinterpret_cast<void *>(g_zstd_dctx_workspace), sizeof(g_zstd_dctx_workspace), reinterpret_cast<void *>(map_base), static_cast<size_t>(map_end - map_base), segment_size, compressed_data_buf, file_size);
R_UNLESS_LOG(decompressed, ldr::ResultInvalidNso(), "[ldr] Failed to decompress segment with zbic!\n");
} 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_UNLESS_LOG(decompressed, ldr::ResultInvalidNso(), "[ldr] Failed to decompress segment with lz4!\n");
}
R_SUCCEED();
@@ -684,12 +706,15 @@ namespace ams::ldr {
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_UNLESS_LOG(std::memcmp(hash, nso_header->segment_hashes[segment], sizeof(hash)) == 0, ldr::ResultInvalidNso(), "[ldr] Invalid segment hash!\n");
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;
const bool is_zbic = (nso_header->flags & NsoHeader::Flag_UseZbicCompression) != 0;
const size_t module_size = static_cast<size_t>(nso_header->rw_dst_offset) + util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
const size_t arena_size = (nso_size > module_size) ? (nso_size - module_size) : 0;
/* Map and read data from file. */
{
@@ -703,11 +728,11 @@ namespace ams::ldr {
/* 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));
(nso_header->flags & NsoHeader::Flag_CompressedText) != 0, is_zbic, 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));
(nso_header->flags & NsoHeader::Flag_CompressedRo) != 0, is_zbic, 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));
(nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, is_zbic, 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);
@@ -730,7 +755,15 @@ namespace ams::ldr {
/* Apply PCV and PTM patches */
if (g_is_pcv) {
hoc::pcv::Patch(map_address, nso_size);
const size_t text_end = static_cast<size_t>(nso_header->text_size);
const size_t rx_end = util::AlignUp(text_end, os::MemoryPageSize);
const size_t ro_start = static_cast<size_t>(nso_header->ro_dst_offset);
const size_t cave_end = (rx_end < ro_start) ? rx_end : ro_start;
const uintptr_t cave = map_address + text_end;
const size_t cave_size = (cave_end > text_end) ? (cave_end - text_end) : 0;
/* module_size is used rather than nso_size to exclude the extra data section. */
hoc::pcv::Patch(map_address, module_size, cave, cave_size, nso_address, arena_size ? (map_address + module_size) : 0);
}
if (g_is_ptm) {
@@ -741,7 +774,7 @@ namespace ams::ldr {
/* Set permissions. */
const size_t text_size = util::AlignUp(nso_header->text_size, os::MemoryPageSize);
const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize);
const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize) + arena_size;
if (text_size) {
const bool prevent_code_reads = (nso_header->flags & NsoHeader::Flag_PreventCodeReads);
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute));
@@ -762,14 +795,15 @@ namespace ams::ldr {
for (int i = 0; i < ctx.nso_count; i++) {
const NsoIndex nso_idx = static_cast<NsoIndex>(ctx.ali.nso_indices[i]);
const bool is_zbic = (ctx.headers[i].flags & NsoHeader::Flag_UseZbicCompression) != 0;
const size_t map_size = is_zbic ? (total_end - process_info->nso_address[i]) : process_info->nso_size[i];
AMS_LOG("[ldr] module[%d]: idx=%d, path='%s', zbic=%d\n", i, (int)nso_idx, GetNsoPath(nso_idx), is_zbic);
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));
}
@@ -812,7 +846,7 @@ 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); };
ON_RESULT_FAILURE { R_DISCARD(svc::CloseHandle(process_handle)); };
/* Load all auto load modules. */
R_RETURN(LoadAutoLoadModules(out, ctx, argument));
@@ -870,8 +904,8 @@ namespace ams::ldr {
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);
const auto as_type = GetProcessAddressSpace(std::addressof(meta));
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::ProcessAddressSpace_64Bit39 || as_type == Npdm::ProcessAddressSpace_64Bit36);
/* Register all NSOs. */
for (int i = 0; i < ctx.nso_count; i++) {
@@ -882,7 +916,7 @@ namespace ams::ldr {
/* If we're overriding for HBL, perform HTML document redirection. */
if (override_status.IsHbl()) {
/* Don't validate result, failure is okay. */
RedirectHtmlDocumentPathForHbl(loc);
R_DISCARD(RedirectHtmlDocumentPathForHbl(loc));
}
/* Clear the external code for the program. */
@@ -26,7 +26,7 @@
#define DISABLED 0
#define DEACTIVATED_GPU_FREQ 2000
#define GPU_MIN_MIN_VOLT 480000
#define CPU_MAX_MAX_VOLT 1200000
#define CPU_MAX_MAX_VOLT 1235000
namespace ams::ldr::hoc {
@@ -40,7 +40,7 @@ volatile CustomizeTable C = {
/* 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,
.stepMode = StepMode_Jedec,
.marikoEmcMaxClock = 2133000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
.marikoEmcVddqVolt = 600000,
@@ -59,10 +59,6 @@ volatile CustomizeTable C = {
.t7_tWTR = 0,
.t8_tREFI = 0,
/* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
/* This is a lazy workaround until I find the issue... */
.t2_tRP_cap = 2,
/* Frequency where non low timings gets used. */
.timingEmcTbreak = DISABLED,
.low_t1_tRCD = 0,
@@ -89,7 +85,7 @@ volatile CustomizeTable C = {
},
.eristaCpuUV = 0,
.eristaCpuVmin = 800,
.eristaCpuVmin = 825,
.eristaCpuMaxVolt = 1200,
/* Unlocks up to 2397 Mhz CPU, usage is not recommended. */
.eristaCpuUnlock = DISABLED,
@@ -104,13 +100,14 @@ volatile CustomizeTable C = {
/* 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. */
/* Supported values: 1963500, 2091000, 2193000, 2295000, 2397000, 2499000, 2601000, 2703000, 2805000. */
/* 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. */
/* 2805000 can only be ran on the best of consoles and may be dangerous due to the voltage requirement */
.marikoCpuMaxClock = 1963500,
.eristaCpuBoostClock = 1785000, /* Default boost clock */
@@ -177,8 +174,8 @@ volatile CustomizeTable C = {
AUTO /* 1075 */,
AUTO /* 1152 (HioPT / High UV only!) */,
AUTO /* 1228 (High UV Only!) */,
DEACTIVATED_GPU_FREQ /* 1267 (Disabled by default) */,
DEACTIVATED_GPU_FREQ /* 1305 (Disabled by default) */,
AUTO /* 1267 (High UV Only!) */,
AUTO /* 1305 (High UV Only!) */,
DEACTIVATED_GPU_FREQ /* 1344 (Disabled by default) */,
DEACTIVATED_GPU_FREQ /* 1382 (Disabled by default) */,
DEACTIVATED_GPU_FREQ /* 1420 (Disabled by default) */,
@@ -215,6 +212,8 @@ volatile CustomizeTable C = {
0 /* >= 3133 */,
0 /* >= 3166 */,
0 /* >= 3200 */,
0 /* >= 3266 */,
0 /* >= 3333 */,
},
/* Advanced. */
@@ -320,6 +319,7 @@ volatile CustomizeTable C = {
{ 2499000, { 1580725, -35815, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2601000, { 1702903, -36675, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2703000, { 1770375, -37515, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2805000, { 1838820, -37707, 113, }, { CPU_MAX_MAX_VOLT, } },
},
.marikoCpuDvfsTable1581Tbreak {
@@ -348,6 +348,7 @@ volatile CustomizeTable C = {
{ 2499000, { 1736856, -35286, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2601000, { 1787838, -35967, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2805000, { 1908891, -37707, 113, }, { CPU_MAX_MAX_VOLT, } },
},
.marikoCpuDvfsTable1683Tbreak {
@@ -376,6 +377,7 @@ volatile CustomizeTable C = {
{ 2499000, { 1736856, -35286, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2601000, { 1787838, -35967, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2805000, { 1908891, -37707, 113, }, { CPU_MAX_MAX_VOLT, } },
},
.marikoCpuDvfsTableExtreme {
@@ -404,6 +406,7 @@ volatile CustomizeTable C = {
{ 2499000, { 1580725, -35815, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2601000, { 1702903, -36675, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2703000, { 1775375, -37515, 113, }, { CPU_MAX_MAX_VOLT, } },
{ 2805000, { 1838820, -37707, 113, }, { CPU_MAX_MAX_VOLT, } },
},
.eristaGpuDvfsTable = {
@@ -434,30 +437,33 @@ volatile CustomizeTable C = {
},
.eristaGpuDvfsTableSLT = {
{ 76800, { }, { 814294, 8144, -940, 0, 0, 226, } },
{ 115200, { }, { 856185, 8144, -940, 0, 0, 226, } },
{ 153600, { }, { 856185, 8144, -940, 0, 0, 226, } },
{ 192000, { }, { 908077, 8144, -940, 0, 0, 226, } },
{ 230400, { }, { 908077, 8144, -940, 0, 0, 226, } },
{ 268800, { }, { 934968, 8144, -940, 0, 0, 226, } },
{ 307200, { }, { 934968, 8144, -940, 0, 0, 226, } },
{ 345600, { }, { 952860, 8144, -940, 0, 0, 226, } },
{ 384000, { }, { 952860, 8144, -940, 0, 0, 226, } },
{ 422400, { }, { 978751, 8144, -940, 0, 0, 226, } },
{ 460800, { }, { 978751, 8144, -940, 0, 0, 226, } },
{ 499200, { }, { 990642, 8144, -940, 0, 0, 226, } },
{ 537600, { }, { 990642, 8144, -940, 0, 0, 226, } },
{ 576000, { }, { 1017534, 8144, -940, 0, 0, 226, } },
{ 614400, { }, { 1017534, 8144, -940, 0, 0, 226, } },
{ 652800, { }, { 1042425, 8144, -940, 0, 0, 226, } },
{ 691200, { }, { 1042425, 8144, -940, 0, 0, 226, } },
{ 729600, { }, { 1066317, 8144, -940, 0, 0, 226, } },
{ 768000, { }, { 1066317, 8144, -940, 0, 0, 226, } },
{ 806400, { }, { 1093208, 8144, -940, 0, 0, 226, } },
{ 844800, { }, { 1093208, 8144, -940, 0, 0, 226, } },
{ 883200, { }, { 1118100, 8144, -940, 0, 0, 226, } },
{ 921600, { }, { 1118100, 8144, -940, 0, 0, 226, } },
{ 960000, { }, { 1156991, 8144, -940, 0, 0, 226, } },
{ 76800, { }, { 700000, } },
{ 153600, { }, { 700000, } },
{ 192000, { }, { 705000, } },
{ 230400, { }, { 710000, } },
{ 268800, { }, { 720000, } },
{ 307200, { }, { 730000, } },
{ 345600, { }, { 740000, } },
{ 384000, { }, { 750000, } },
{ 422400, { }, { 760000, } },
{ 460800, { }, { 770000, } },
{ 499200, { }, { 780000, } },
{ 537600, { }, { 790000, } },
{ 576000, { }, { 800000, } },
{ 614400, { }, { 810000, } },
{ 652800, { }, { 820000, } },
{ 691200, { }, { 830000, } },
{ 729600, { }, { 850000, } },
{ 768000, { }, { 860000, } },
{ 806400, { }, { 875000, } },
{ 844800, { }, { 885000, } },
{ 883200, { }, { 900000, } },
{ 921600, { }, { 910000, } },
{ 960000, { }, { 930000, } },
// { 998400, { }, { 945000, } },
// { 1036800, { }, { } },
// { 1152000, { }, { } },
// { 1075200, { }, { } },
},
.eristaGpuDvfsTableHiOPT = {
@@ -568,8 +574,15 @@ volatile CustomizeTable C = {
{ 998400, { }, { 1065665, -16075, -497, -179, 3213, 9 } },
{ 1075200, { }, { 1132576, -16093, -648, 0, 1077, 40 } },
{ 1152000, { }, { 1180029, -14534, -830, 0, 1469, 110 } },
// { 1228800, { }, { 1238293, -16383, -859, 0, 3722, 313 } },
// { 1267200, { }, { 1276399, -17475, -867, 0, 3681, 559 } },
{ 1228800, { }, { 1238293, -16383, -859, 0, 3722, 313 } },
{ 1267200, { }, { 1276399, -17475, -867, 0, 3681, 559 } },
{ 1305600, { }, { } },
{ 1344000, { }, { } },
{ 1382400, { }, { } },
{ 1420800, { }, { } },
{ 1459200, { }, { } },
{ 1497600, { }, { } },
{ 1536000, { }, { } },
},
.marikoGpuDvfsTableHighUV = {
@@ -20,8 +20,8 @@
#pragma once
#define CUST_REV 5
#define KIP_VERSION 242
#define CUST_REV 7
#define KIP_VERSION 260
#include "oc_common.hpp"
#include "pcv/pcv_common.hpp"
@@ -86,8 +86,6 @@ struct CustomizeTable {
u32 t7_tWTR;
u32 t8_tREFI;
u32 t2_tRP_cap;
u32 timingEmcTbreak;
u32 low_t1_tRCD;
u32 low_t2_tRP;
@@ -128,7 +126,7 @@ struct CustomizeTable {
u32 eristaGpuVoltArray[27];
u32 marikoGpuVoltArray[24];
s32 marikoSocVoltArray[26];
s32 marikoSocVoltArray[28];
u32 fineTune_t6_tRTW;
u32 fineTune_t7_tWTR;
@@ -135,14 +135,9 @@ namespace ams::ldr::hoc::pcv::mariko {
/* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
/* This is a lazy workaround until I find the issue... */
const bool lowFreq = freq < C.timingEmcTbreak;
volatile u32 tRPpbIndex = lowFreq ? C.low_t2_tRP : C.t2_tRP;
if (WL == WL_1331) {
tRPpbIndex = MIN(C.t2_tRP_cap, tRPpbIndex);
}
tRCD = tRCD_values[lowFreq ? C.low_t1_tRCD : C.t1_tRCD];
tRPpb = tRP_values[tRPpbIndex];
tRPpb = tRP_values[lowFreq ? C.low_t2_tRP : C.t2_tRP];
tRAS = tRAS_values[lowFreq ? C.low_t3_tRAS : C.t3_tRAS];
tRRD = tRRD_values[lowFreq ? C.low_t4_tRRD : C.t4_tRRD];
tRFCpb = tRFC_values[lowFreq ? C.low_t5_tRFC : C.t5_tRFC];
@@ -24,35 +24,39 @@
#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
#include "oc_log.hpp"
#define PATCH_OFFSET(offset, value) \
static_assert(sizeof(__typeof__(offset)) <= sizeof(u64)); \
*(offset) = value;
namespace ams::ldr {
R_DEFINE_ERROR_RESULT(OutOfRange, 1000);
R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001);
R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002);
R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003);
R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004);
R_DEFINE_ERROR_RESULT(InvalidCpuFreqVddEntry, 1005);
R_DEFINE_ERROR_RESULT(InvalidCpuVoltDfllEntry, 1006);
R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1007);
R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1008);
R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1009);
R_DEFINE_ERROR_RESULT(InvalidGpuFreqMaxPattern, 1010);
R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1011);
R_DEFINE_ERROR_RESULT(InvalidRegulatorEntry, 1012);
R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013);
R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014);
R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015);
R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017);
R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018);
R_DEFINE_ERROR_RESULT(OutOfRange, 1000);
R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001);
R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002);
R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003);
R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004);
R_DEFINE_ERROR_RESULT(InvalidCpuFreqVddEntry, 1005);
R_DEFINE_ERROR_RESULT(InvalidCpuVoltDfllEntry, 1006);
R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1007);
R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1008);
R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1009);
R_DEFINE_ERROR_RESULT(InvalidGpuFreqMaxPattern, 1010);
R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1011);
R_DEFINE_ERROR_RESULT(InvalidRegulatorEntry, 1012);
R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013);
R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014);
R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015);
R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017);
R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018);
R_DEFINE_ERROR_RESULT(HookArenaOutOfMemory, 1019);
R_DEFINE_ERROR_RESULT(HookPayloadTooLarge, 1020);
R_DEFINE_ERROR_RESULT(HookRelocationUnsupported, 1021);
R_DEFINE_ERROR_RESULT(HookSiteInvalid, 1022);
R_DEFINE_ERROR_RESULT(HookPayloadEscapes, 1023);
R_DEFINE_ERROR_RESULT(HookDataOutOfMemory, 1024);
R_DEFINE_ERROR_RESULT(HookUnavailable, 1025);
}
namespace ams::ldr::hoc {
@@ -21,6 +21,7 @@
namespace ams::ldr::hoc {
Result SmcCopyToIram(uintptr_t dest, const void *src, u32 size);
void Log(const char *data, ...);
void ViewLog();
@@ -19,6 +19,7 @@
*/
#include "pcv.hpp"
#include "pcv_hook.hpp"
namespace ams::ldr::hoc::pcv {
@@ -43,7 +44,7 @@ namespace ams::ldr::hoc::pcv {
constexpr u32 uv_step = 12'500;
constexpr u32 uv_min = 600'000;
auto validator = [](regulator* entry) {
auto validator = [](regulator *entry) {
R_UNLESS(entry->id == 1, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type == 1, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type_1.volt_reg == 0x17, ldr::ResultInvalidRegulatorEntry());
@@ -76,14 +77,14 @@ namespace ams::ldr::hoc::pcv {
}
void SafetyCheck() {
struct sValidator {
struct Validator {
volatile u32 value;
u32 min;
u32 max;
u32 panic;
bool value_required = false;
Result check() {
Result Check() {
if (!value_required && !value) {
R_SUCCEED();
}
@@ -145,40 +146,50 @@ namespace ams::ldr::hoc::pcv {
break;
}
sValidator validators[] = {
{ C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true },
{ C.marikoCpuBoostClock, 1020'000, 2703'000, panic::Cpu, true },
{ C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, },
{ C.marikoCpuMaxVolt, 1000, 1200, panic::Cpu, },
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, },
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000, panic::Cpu, },
{ C.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, }, /* Official vmax for the RAMs is 1400-1500mV */
{ C.eristaEmcMaxClock, 1600'000, 2600'000, panic::Emc, },
{ C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, },
{ C.marikoEmcVddqVolt, 400'000, 750'000, panic::Emc, },
{ C.marikoSocVmax, 1000, 1200, panic::Emc, },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, panic::Gpu, },
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000, panic::Gpu, },
{ C.marikoGpuVmax, 800, 960, panic::Gpu, },
Validator validators[] = {
{ C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true },
{ C.marikoCpuBoostClock, 1020'000, 2805'000, panic::Cpu, true },
{ C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, },
{ C.marikoCpuMaxVolt, 1000, 1235, panic::Cpu, },
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, },
{ marikoCpuDvfsMaxFreq, 1785'000, 2805'000, panic::Cpu, },
{ C.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, }, /* Official vmax for the RAMs is 1400-1500mV */
{ C.eristaEmcMaxClock, 1600'000, 2600'000, panic::Emc, },
{ C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, },
{ C.marikoEmcVddqVolt, 400'000, 750'000, panic::Emc, },
{ C.marikoSocVmax, 1000, 1200, panic::Emc, },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, panic::Gpu, },
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000, panic::Gpu, },
{ C.marikoGpuVmax, 800, 960, panic::Gpu, },
};
for (auto &v : validators) {
if (R_FAILED(v.check())) {
if (R_FAILED(v.Check())) {
panic::SmcError(v.panic);
CRASH("Validation FAIL");
}
}
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
void WriteKipLoadToIram() {
const u32 hocMagic = 0x686F634D;
constexpr uintptr_t LoadMagicAddress = 0x4003DC00; /* Should be a pretty safe address. */
R_DISCARD(SmcCopyToIram(LoadMagicAddress, &hocMagic, sizeof(hocMagic)));
}
void Patch(uintptr_t mapped_nso, size_t nso_size, uintptr_t cave, size_t cave_size, uintptr_t nso_address, uintptr_t data_arena) {
SafetyCheck();
Hooks().Initialize(mapped_nso, nso_address, cave, cave_size, data_arena);
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
if (isMariko) {
mariko::Patch(mapped_nso, nso_size);
} else {
erista::Patch(mapped_nso, nso_size);
}
WriteKipLoadToIram();
}
}
@@ -24,6 +24,7 @@
#include "pcv_common.hpp"
#include "pcv_erista.hpp"
#include "pcv_mariko.hpp"
#include "pcv_hook.hpp"
namespace ams::ldr::hoc::pcv {
@@ -202,6 +203,6 @@ namespace ams::ldr::hoc::pcv {
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
void SafetyCheck();
void Patch(uintptr_t mapped_nso, size_t nso_size);
void Patch(uintptr_t mapped_nso, size_t nso_size, uintptr_t cave, size_t cave_size, uintptr_t nso_address, uintptr_t data_arena);
}
@@ -114,7 +114,6 @@ namespace ams::ldr::hoc::pcv {
bool secondMatch = (ins2 & StpRegsImmMask) == (cmp2 & StpRegsImmMask);
constexpr u32 MovMask = ~((1u << 5) - 1u);
bool thirdMatch = (ins3 & MovMask) == (cmp3 & MovMask);
@@ -188,4 +187,25 @@ namespace ams::ldr::hoc::pcv {
return (ins1 & ClearImm19) == (ins2 & ClearImm19);
};
inline bool AsmIsFramePush(u32 ins) {
constexpr u32 FramePushMask = 0xFFC07FFF;
constexpr u32 FramePushValue = 0xA9807BFD;
return (ins & FramePushMask) == FramePushValue;
}
inline u32 *FindFnPrologue(u32 *ptr, u32 margin, u32 *nsoStart) {
for (u32 i = 0; i <= margin; ++i) {
u32 *candidate = ptr - i;
if (candidate < nsoStart) {
break;
}
if (AsmIsFramePush(*candidate)) {
return candidate;
}
}
return nullptr;
}
}
@@ -48,6 +48,35 @@ namespace ams::ldr::hoc::pcv {
u64 dvco_calibration_max;
};
/*
struct CvbCpuDfllData {
u32 tune0_low;
u32 tune0_high;
u32 tune1_low;
u32 tune1_high;
u64 padding;
u64 dvco_calibration_max;
};
struct CpuCvbTable {
u64 socType;
CvbCpuDfllData dfllData;
u64 padding[3];
u64 unk;
u32 tune_high_min_millivolts;
u32 tune_high_margin_millivolts;
u32 vmin;
u32 padding_1[3];
u32 pll_min_millivolts;
u32 vmax;
u32 unkScale;
u32 speedoScale;
u32 voltageScale;
u32 zero;
cvb_entry_t cvb_entry[32];
};
*/
struct __attribute__((packed)) div_nmp {
u8 divn_shift;
u8 divn_width;
@@ -27,9 +27,25 @@
namespace ams::ldr::hoc::pcv::erista {
std::vector<u32> newEmcList;
u32 *nsoStart;
u32 *nsoEnd;
struct HookPayloadData {
struct {
EristaMtcTable *mtcTable;
u32 mtcCount;
} mtcTableAsm;
};
DEFINE_HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
namespace {
std::vector<u32> newEmcList;
u32 *nsoStart;
u32 *nsoEnd;
struct {
u32 *getEristaMtcTableFnSite = nullptr;
EristaMtcTable *mtcTable = nullptr;
bool foundMtcTablePattern = false;
} getMtcTableCache;
}
Result CpuVoltDvfs(u32 *ptr) {
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
@@ -322,7 +338,16 @@ namespace ams::ldr::hoc::pcv::erista {
da_covers |= (w_cover << 16);
table->burst_mc_regs.mc_emem_arb_da_covers = da_covers;
table->burst_mc_regs.mc_emem_arb_misc0 = (table->burst_mc_regs.mc_emem_arb_misc0 & 0xFFE08000) | (table->burst_mc_regs.mc_emem_arb_timing_rc + 1);
constexpr u32 AtomsPerDvfsPulse = 0x7;
constexpr u32 McEmcSameFreq = 0x0;
constexpr u32 ExpiringSoonSlackThreshold = 0xC;
const u32 priorityInversionIsoThreshold = GET_CYCLE_CEIL(7.5);
constexpr u32 EmcReqB2bXfer = 0x0;
const u32 priorityInversionThreshold = GET_CYCLE_CEIL(22.5);
const u32 bc2aaHoldoffThreshold = table->burst_mc_regs.mc_emem_arb_timing_rc + 1;
const u32 mc_emem_arb_misc0 = (AtomsPerDvfsPulse << 28) | (McEmcSameFreq << 27) | (ExpiringSoonSlackThreshold << 21) | (priorityInversionIsoThreshold << 16) | (EmcReqB2bXfer << 15) | (priorityInversionThreshold << 8) | (bc2aaHoldoffThreshold << 0);
table->burst_mc_regs.mc_emem_arb_misc0 = mc_emem_arb_misc0;
u32 mpcorer_ptsa_rate = MIN(static_cast<u32>(227), (table->rate_khz / 1600000) * 208);
table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = mpcorer_ptsa_rate;
@@ -363,12 +388,16 @@ namespace ams::ldr::hoc::pcv::erista {
table->la_scale_regs.mc_latency_allowance_hc_1 = (table->la_scale_regs.mc_latency_allowance_hc_1 & Mask2) | allowance1;
table->la_scale_regs.mc_latency_allowance_vi2_0 = (table->la_scale_regs.mc_latency_allowance_vi2_0 & Mask2) | allowance1;
table->dram_timings.t_rp = tRFCpb;
table->dram_timings.t_rfc = tRFCab;
table->emc_cfg_2 = 0x11083D;
table->min_volt = std::clamp(900 + (C.emcDvbShift * 25), 900, 1050);
table->dram_timings.t_rp = tRP_values[0];
const u32 tRFCabStock = tRFC_values[0] * 2;
table->dram_timings.t_rfc = tRFCabStock;
table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast<u32>(mrw2);
table->emc_mrw = (table->emc_mrw & ~0x70u) | 0x40; /* nWR */
table->emc_cfg_2 = 0x11083D;
table->min_volt = std::clamp(900 + (C.emcDvbShift * 25), 900, 1050);
}
/* TODO: Template this */
Result VerifyMtcTable(EristaMtcTable *tableStart, u32 expectedFreq) {
R_UNLESS(tableStart->rate_khz == expectedFreq, ldr::ResultInvalidMtcTable());
@@ -434,39 +463,6 @@ namespace ams::ldr::hoc::pcv::erista {
}
}
/* Relocate the table */
/* Rescanning is simpler than trying to extract a bunch of data from the asm patch, performance impact is negligable */
/* Also, this is more stable :P */
u32 RepointEristaEmcTablePtr(uintptr_t fromSlot, uintptr_t toTable) {
constexpr u32 RetIns = 0xD65F03C0; /* ret */
u32 patched = 0;
for (u32 *p = nsoStart; p + 4 < nsoEnd; ++p) {
const u32 ins = *p;
if (!AsmIsAdrX0(ins)) {
continue;
}
const uintptr_t pc = reinterpret_cast<uintptr_t>(p);
if (AsmAdrTarget(ins, pc) != fromSlot) {
continue;
}
if (!(AsmIsLdpX(p[1]) && AsmIsLdpX(p[2]) && p[3] == RetIns)) {
continue;
}
/* adr only reaches +-1MB */
const s64 delta = static_cast<s64>(toTable) - static_cast<s64>(pc);
if (delta > 0xFFFFF || delta < -0x100000) {
continue;
}
PATCH_OFFSET(p, AsmSetAdrTarget(ins, pc, toTable));
++patched;
}
return patched;
}
/* The silicon instructs; the children obey... */
void MtcGenerateFreqTables() {
newEmcList.clear();
@@ -535,24 +531,20 @@ namespace ams::ldr::hoc::pcv::erista {
constexpr u32 StartAdjustment = offsetof(EristaMtcTable, rate_khz) + sizeof(EristaMtcTable) * (erista::MtcTableCountDefault - 1);
u8 *startPtr = reinterpret_cast<u8 *>(ptr) - StartAdjustment;
const uintptr_t usedSlot = reinterpret_cast<uintptr_t>(startPtr) + mtcOffset;
EristaMtcTable *table = reinterpret_cast<EristaMtcTable *>(usedSlot);
EristaMtcTable *table = reinterpret_cast<EristaMtcTable *>(startPtr + mtcOffset);
R_TRY(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault));
PrepareMtcMemoryRegion(startPtr, table);
table = reinterpret_cast<EristaMtcTable *>(startPtr);
/* We must do this as the NLE tables don't have enough space past them for our extended ones */
if (usedSlot != reinterpret_cast<uintptr_t>(startPtr)) {
if (RepointEristaEmcTablePtr(usedSlot, reinterpret_cast<uintptr_t>(startPtr)) == 0) {
AbortInvalidMtc("Failed to repoint emc table");
}
}
if (R_FAILED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault))) {
AbortInvalidMtc("Failed mtc validation");
}
/* Cache the table for hooks. */
getMtcTableCache.mtcTable = table;
if (C.eristaEmcMaxClock <= EmcClkOSLimit) {
R_SKIP();
}
@@ -607,50 +599,71 @@ namespace ams::ldr::hoc::pcv::erista {
// R_SUCCEED();
// }
HOOK_PAYLOAD_FN EristaMtcTable *GetEristaMtcTableImpl(u32 *count) {
const HookPayloadData *data = HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
*count = data->mtcTableAsm.mtcCount;
return data->mtcTableAsm.mtcTable;
}
Result MemMtcTableAsm(u32 *ptr) {
/* Return if the pattern was already found. */
/* This pattern happens multiple times in this function., we only need to find it once. */
R_UNLESS(!getMtcTableCache.foundMtcTablePattern, ldr::ResultInvalidMtcTablePattern());
/* This is a mess but the compiler made this painful to patch so we must do it this way */
constexpr s32 GoodAdrpOffset = -1;
constexpr s32 GoodMovOffset = -7;
constexpr s32 GoodBlOffset = 1;
constexpr u32 MtcGoodBlOpcode = 0x97fe6cfc;
constexpr u32 MtcBadBlOpcode0 = 0x97ffae64; // bl nn::pcv::GetHardwareType
constexpr u32 MtcBadBlOpcode1 = 0x940036d5; // bl strcmp
constexpr u32 MtcBadAdrpAsm = 0xd00000a1; // adrp x1, s_ModuleResetStatus_
constexpr s32 MtcBadBlOffset0 = 2;
constexpr s32 MtcBadBlOffset1 = -1;
constexpr s32 MtcBadAdrpOffset = 1;
constexpr u32 AddrpOffset = 1;
constexpr u32 MovOffset = 7;
constexpr u32 BlOffset = 5;
constexpr u32 MovOffsetOld = 8;
/* Ensure we don't dereference memory before nso start. */
R_UNLESS(ptr + GoodMovOffset >= nsoStart, ldr::ResultInvalidMtcTablePattern());
R_UNLESS(ptr - MovOffset >= nsoStart, ldr::ResultInvalidMtcTablePattern());
/* Check for GetHardwareType asm and skip if it is found */
/* The pattern will match on the first time, but the location is bad, so it must be skipped */
if(AsmCompareAdrpNoImm(*(ptr + MtcBadAdrpOffset), MtcBadAdrpAsm) && AsmBlCompareOpcodeOnly(*(ptr + MtcBadBlOffset0), MtcBadBlOpcode0) && AsmBlCompareOpcodeOnly(*(ptr + MtcBadBlOffset1), MtcBadBlOpcode1)) {
R_SKIP();
}
/* We don't check for matching register because both registers must be x0 in order to pass the previous checks. */
/* The correct instructions will always be x0 since the mtcTable pointer is returned. */
u32 adrp = *(ptr + GoodAdrpOffset);
u32 adrp = *(ptr - AddrpOffset);
R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTablePattern());
/* Check for the branch instruction above the cbz to ensure we are patching the right location*/
u32 bl = *(ptr + GoodBlOffset);
R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcGoodBlOpcode), ldr::ResultInvalidMtcTablePattern());
u32 bl = *(ptr - BlOffset);
R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcBlIns), ldr::ResultInvalidMtcTablePattern());
/* Check for the mov that actually sets the mtc table count. */
u32 mov = *(ptr + GoodMovOffset);
R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTablePattern());
u32 mov = *(ptr - MovOffset);
bool foundMov = false;
foundMov = asm_compare_no_rd(mov, MtcMovAsm);
/* Patch out the count of the mov to our custom mtc table amount*/
u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), asm_get_rd(mov));
if (!foundMov) {
mov = *(ptr + MovOffsetOld);
/* Check old firmware offset. */
foundMov = asm_compare_no_rd(mov, MtcMovAsm);
}
PATCH_OFFSET(ptr + GoodMovOffset, movCountPatch);
R_UNLESS(foundMov, ldr::ResultInvalidMtcTablePattern());
constexpr u32 PrologueWindow = 140;
u32 *functionPrologue = FindFnPrologue(ptr, PrologueWindow, nsoStart);
R_UNLESS(functionPrologue != nullptr, ldr::ResultInvalidMtcTablePattern());
getMtcTableCache.getEristaMtcTableFnSite = functionPrologue;
getMtcTableCache.foundMtcTablePattern = true;
R_SUCCEED();
}
Result InstallHooks() {
R_TRY(Hooks().CheckEnabled());
R_TRY(Hooks().CopyPayload());
R_UNLESS(getMtcTableCache.getEristaMtcTableFnSite != nullptr && getMtcTableCache.mtcTable != nullptr, ldr::ResultInvalidMtcTablePattern());
/* Copy the data to the payload. */
auto *data = Hooks().BindData(e_HookPayloadData);
R_UNLESS(data != nullptr, ldr::ResultHookDataOutOfMemory());
data->mtcTableAsm.mtcTable = reinterpret_cast<EristaMtcTable *>(Hooks().ToVa(getMtcTableCache.mtcTable));
data->mtcTableAsm.mtcCount = newEmcList.size();
R_TRY(INSTALL_IMPL_HOOK(getMtcTableCache.getEristaMtcTableFnSite, GetEristaMtcTableImpl));
R_SUCCEED();
}
@@ -672,7 +685,7 @@ namespace ams::ldr::hoc::pcv::erista {
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq },
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
{"GPU PLL Max", & GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
{"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
// {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
{"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn },
{"MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
@@ -690,17 +703,18 @@ namespace ams::ldr::hoc::pcv::erista {
}
}
// ViewLog();
for (auto &entry : patches) {
LOGGING("%s Count: %zu\n", entry.description, entry.patched_count);
if (R_FAILED(entry.CheckResult())) {
// ViewLog();
panic::SmcError(panic::Patch);
CRASH(entry.description);
}
}
if (R_FAILED(InstallHooks())) {
panic::SmcError(panic::Patch);
}
}
}
@@ -140,7 +140,7 @@ namespace ams::ldr::hoc::pcv::erista {
MtcTableIndex index;
};
/* TODO: Test 6gb and 8gb. */
/* TODO: Test 6gb and 8gb. */
const inline MtcDramIndex mtcIndexTable[] = {
{ ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, },
{ ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC, T210SdevEmcDvfsTableS4gb01, },
@@ -0,0 +1,364 @@
/*
* 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
#include "../oc_common.hpp"
#define HOOK_PAYLOAD_FN __attribute__((section("hoc_hookpayload"), used, noinline, visibility("hidden")))
/* Inline asm because fuck compilers: GCC refuses to put a variable and a function in the same section. */
/* It wants alloc+write for one and alloc+exec for the other. */
/* You are supposed to be able to override that by writing the flags into the section name yourself, */
/* like section("hoc_hookpayload,\"ax\",%progbits"), instead of letting GCC pick them. */
/* But GCC takes that whole string as the name and never reads the flags out of it, so it appends the */
/* ones it wanted anyway: the .section directive it emits ends up carrying two sets of flags, and the */
/* assembler rejects it. The failure lands in the generated assembly, not in anything we wrote. */
/* Therefore we must use inline assembly, which reaches the assembler exactly as written. */
#define DEFINE_HOOK_PAYLOAD_PTR(type, name) \
asm(".section hoc_hookpayload,\"ax\",%progbits\n" \
".balign 8\n" \
".global " #name "\n" \
".hidden " #name "\n" \
#name ": .zero 8\n" \
".text\n"); \
extern "C" __attribute__((visibility("hidden"))) type *name
#define HOOK_PAYLOAD_PTR(type, name) \
([]() -> type * { \
type **_hoc_pp; \
__asm__("adr %0, " #name : "=r"(_hoc_pp)); \
return *_hoc_pp; \
}())
extern "C" const u8 __start_hoc_hookpayload[];
extern "C" const u8 __stop_hoc_hookpayload[];
namespace ams::ldr::hoc::pcv {
constexpr size_t HookPageSize = 0x1000;
constexpr size_t PcvDataArenaSize = 0x1000;
inline s64 SignExtend(u64 value, int bits) {
const int shift = 64 - bits;
return static_cast<s64>(value << shift) >> shift;
}
inline u32 EncodeRelBranch(u32 opc, uintptr_t site_va, uintptr_t target_va) {
const s64 delta = static_cast<s64>(target_va) - static_cast<s64>(site_va);
AMS_ABORT_UNLESS((delta & 0x3) == 0);
AMS_ABORT_UNLESS(delta >= -0x08000000 && delta <= 0x07FFFFFC);
return opc | (static_cast<u32>(delta >> 2) & 0x03FFFFFFu);
}
inline u32 EncodeB(uintptr_t site_va, uintptr_t target_va) { return EncodeRelBranch(0x14000000u, site_va, target_va); }
inline u32 EncodeBL(uintptr_t site_va, uintptr_t target_va) { return EncodeRelBranch(0x94000000u, site_va, target_va); }
inline u32 EncodePairSp(bool load, u32 rt1, u32 rt2, s32 imm) {
const u32 base = load ? 0xA9400000u : 0xA9000000u;
const u32 imm7 = static_cast<u32>((imm / 8) & 0x7F);
return base | (imm7 << 15) | (rt2 << 10) | (31u << 5) | rt1;
}
inline u32 EncodeSpAdjust(bool sub, u32 imm12) {
const u32 base = sub ? 0xD1000000u : 0x91000000u;
return base | ((imm12 & 0xFFFu) << 10) | (31u << 5) | 31u;
}
inline Result RelocateInstruction(u32 insn, uintptr_t old_site_va, uintptr_t new_site_va, u32 *out) {
const u32 top6 = insn & 0xFC000000u;
if (top6 == 0x14000000u || top6 == 0x94000000u) {
const s64 old_imm = SignExtend(static_cast<u64>(insn & 0x03FFFFFFu) << 2, 28);
const uintptr_t target = old_site_va + old_imm;
*out = EncodeRelBranch(top6, new_site_va, target);
R_SUCCEED();
}
R_UNLESS((insn & 0xFF000010u) != 0x54000000u, ldr::ResultHookRelocationUnsupported()); /* b.cond */
R_UNLESS((insn & 0x7E000000u) != 0x34000000u, ldr::ResultHookRelocationUnsupported()); /* cbz/cbnz */
R_UNLESS((insn & 0x7E000000u) != 0x36000000u, ldr::ResultHookRelocationUnsupported()); /* tbz/tbnz */
R_UNLESS((insn & 0x1F000000u) != 0x10000000u, ldr::ResultHookRelocationUnsupported()); /* adr/adrp */
R_UNLESS((insn & 0x3B000000u) != 0x18000000u, ldr::ResultHookRelocationUnsupported()); /* ldr literal */
*out = insn;
R_SUCCEED();
}
class HookContext {
private:
uintptr_t m_map_base = 0; /* loader-side base of pcv's NSO mapping. */
uintptr_t m_va_base = 0; /* pcv-side base of the same memory. */
uintptr_t m_cave = 0; /* loader-side cave base. (0 if unavailable) */
size_t m_cave_size = 0;
uintptr_t m_payload = 0; /* loader-side base of the payload copy. */
size_t m_payload_sz = 0;
size_t m_used = 0;
uintptr_t m_data = 0; /* loader-side data arena base, 0 if none. */
size_t m_data_used = 0;
public:
constexpr HookContext() = default;
void Initialize(uintptr_t map_base, uintptr_t va_base, uintptr_t cave, size_t cave_size, uintptr_t data) {
m_map_base = map_base;
m_va_base = va_base;
m_cave = cave;
m_cave_size = cave_size;
m_payload = 0;
m_payload_sz = 0;
m_used = 0;
m_data = data;
m_data_used = 0;
}
bool IsEnabled() const { return m_cave != 0 && m_cave_size != 0; }
Result CheckEnabled() const {
R_UNLESS(this->IsEnabled(), ldr::ResultHookUnavailable());
R_SUCCEED();
}
size_t CaveSize() const { return m_cave_size; }
size_t CaveUsed() const { return m_used; }
size_t CaveFree() const { return m_cave_size > m_used ? m_cave_size - m_used : 0; }
size_t DataSize() const { return m_data != 0 ? PcvDataArenaSize : 0; }
size_t DataUsed() const { return m_data_used; }
size_t DataFree() const { return this->DataSize() - m_data_used; }
/* Convert loader mapped address to pcv-side address. */
uintptr_t ToVa(const void *loader_ptr) const {
return m_va_base + (reinterpret_cast<uintptr_t>(loader_ptr) - m_map_base);
}
uintptr_t CaveVa() const { return ToVa(reinterpret_cast<const void *>(m_cave)); }
Result CopyPayload() {
R_TRY(this->CheckEnabled());
const size_t size = static_cast<size_t>(__stop_hoc_hookpayload - __start_hoc_hookpayload);
/* Zero length: Linker garbage collected the payload .(happens when nothing references it) */
/* __start_ and _stop_ don't prevent this. */
/* Copying here would succeed at first but fail later by jumping to empty memory. */
R_UNLESS(size != 0, ldr::ResultUninitializedPatcher());
R_UNLESS(size <= m_cave_size, ldr::ResultHookPayloadTooLarge());
m_payload = m_cave;
m_payload_sz = size;
std::memcpy(reinterpret_cast<void *>(m_payload), __start_hoc_hookpayload, size);
m_used = util::AlignUp(size, sizeof(u32));
R_SUCCEED();
}
/* pcv-side address of a payload symbol's copy. */
uintptr_t PayloadVa(const void *loader_sym) const {
const uintptr_t offset = reinterpret_cast<uintptr_t>(loader_sym) - reinterpret_cast<uintptr_t>(__start_hoc_hookpayload);
return this->ToVa(reinterpret_cast<void *>(m_payload + offset));
}
/* Loader-side, writable pointer to a payload variable's copy in the cave. */
template<typename T>
T *PayloadCopyOf(T &loader_sym) const {
const uintptr_t offset = reinterpret_cast<uintptr_t>(std::addressof(loader_sym)) - reinterpret_cast<uintptr_t>(__start_hoc_hookpayload);
return reinterpret_cast<T *>(m_payload + offset);
}
/* Reserves space in the writable data arena past pcv's .bss. */
/* Returns a zeroed, loader-side pointer. */
/* The data arena is too far from the cave to be addressed directly by symbol, so we must store a pointer to it in the cave section. */
template<typename T>
T *DataAlloc() {
const size_t size = util::AlignUp(sizeof(T), alignof(T) > 8 ? alignof(T) : 8);
if (m_data == 0 || m_data_used + size > PcvDataArenaSize) {
return nullptr;
}
T *p = reinterpret_cast<T *>(m_data + m_data_used);
m_data_used += size;
std::memset(p, 0, sizeof(T));
return p;
}
template<typename T>
T *BindData(T *&loader_sym) {
T *block = this->DataAlloc<T>();
if (block != nullptr) {
*this->PayloadCopyOf(loader_sym) = reinterpret_cast<T *>(this->ToVa(block));
}
return block;
}
/* Replaces the function entirely starting at function prologue. */
/* Preserves the function arguments. */
Result InstallImpl(u32 *site, const void *fn, uintptr_t *out_orig = nullptr) {
R_TRY(this->CheckEnabled());
R_UNLESS(site != nullptr, ldr::ResultHookSiteInvalid());
R_UNLESS(m_payload != 0, ldr::ResultUninitializedPatcher());
R_TRY(this->ValidatePayloadFn(fn));
if (out_orig != nullptr) {
u32 *tramp = this->AllocCode(2);
R_UNLESS(tramp != nullptr, ldr::ResultHookArenaOutOfMemory());
u32 relocated;
R_TRY(RelocateInstruction(site[0], this->ToVa(site), this->ToVa(&tramp[0]), std::addressof(relocated)));
tramp[0] = relocated;
tramp[1] = EncodeB(this->ToVa(&tramp[1]), this->ToVa(site) + sizeof(u32));
*out_orig = this->ToVa(tramp);
}
site[0] = EncodeB(this->ToVa(site), this->PayloadVa(fn));
R_SUCCEED();
}
/* Takes the same arguments as the hooked function, does not replace. */
Result InstallIntercept(u32 *site, const void *fn) {
R_TRY(this->CheckEnabled());
R_UNLESS(site != nullptr, ldr::ResultHookSiteInvalid());
R_UNLESS(m_payload != 0, ldr::ResultUninitializedPatcher());
R_TRY(this->ValidatePayloadFn(fn));
/* x0-x7: arguments */
/* x8: result pointer */
/* x18: platform register */
/* x30: return address */
/* x29: keeps pairs clean */
/* x9-x17: scratch */
constexpr u32 FrameSize = 0x60;
constexpr u32 Pairs[][2] = { {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 18}, {29, 30} };
constexpr u32 PairCount = sizeof(Pairs) / sizeof(Pairs[0]);
constexpr u32 StubWords = 1 + PairCount + 1 + PairCount + 1 + 1 + 1;
u32 *stub = this->AllocCode(StubWords);
R_UNLESS(stub != nullptr, ldr::ResultHookArenaOutOfMemory());
u32 i = 0;
stub[i++] = EncodeSpAdjust(true, FrameSize);
for (u32 p = 0; p < PairCount; ++p) {
stub[i++] = EncodePairSp(false, Pairs[p][0], Pairs[p][1], static_cast<s32>(p * 16));
}
stub[i] = EncodeBL(this->ToVa(&stub[i]), this->PayloadVa(fn));
++i;
for (u32 p = 0; p < PairCount; ++p) {
stub[i++] = EncodePairSp(true, Pairs[p][0], Pairs[p][1], static_cast<s32>(p * 16));
}
stub[i++] = EncodeSpAdjust(false, FrameSize);
u32 relocated;
R_TRY(RelocateInstruction(site[0], this->ToVa(site), this->ToVa(&stub[i]), std::addressof(relocated)));
stub[i] = relocated;
++i;
stub[i] = EncodeB(this->ToVa(&stub[i]), this->ToVa(site) + sizeof(u32));
++i;
AMS_ABORT_UNLESS(i == StubWords);
site[0] = EncodeB(this->ToVa(site), this->ToVa(stub));
R_SUCCEED();
}
/* Checks entry to first ret that everything still points to valid data. */
Result ValidatePayloadFn(const void *fn) const {
constexpr u32 MaxInstructions = 512;
constexpr u32 RetInsn = 0xD65F03C0u;
const uintptr_t lo = m_payload;
const uintptr_t hi = m_payload + m_payload_sz;
const u32 *insns = reinterpret_cast<const u32 *>(fn);
const uintptr_t base = m_payload + (reinterpret_cast<uintptr_t>(fn) - reinterpret_cast<uintptr_t>(__start_hoc_hookpayload));
auto check = [&](uintptr_t target) -> Result {
if (target < lo || target >= hi) {
R_THROW(ldr::ResultHookPayloadEscapes());
}
R_SUCCEED();
};
for (u32 i = 0; i < MaxInstructions; ++i) {
const u32 insn = insns[i];
const uintptr_t site = base + i * sizeof(u32);
if (insn == RetInsn) {
R_SUCCEED();
}
const u32 top6 = insn & 0xFC000000u;
if (top6 == 0x14000000u || top6 == 0x94000000u) { /* b / bl */
const uintptr_t target = site + SignExtend(static_cast<u64>(insn & 0x03FFFFFFu) << 2, 28);
R_TRY(check(target));
} else if ((insn & 0xFF000010u) == 0x54000000u || /* b.cond */
(insn & 0x7E000000u) == 0x34000000u || /* cbz / cbnz */
(insn & 0x3B000000u) == 0x18000000u) { /* ldr literal */
const uintptr_t target = site + SignExtend(static_cast<u64>((insn >> 5) & 0x7FFFFu) << 2, 21);
R_TRY(check(target));
} else if ((insn & 0x7E000000u) == 0x36000000u) { /* tbz / tbnz */
const uintptr_t target = site + SignExtend(static_cast<u64>((insn >> 5) & 0x3FFFu) << 2, 16);
R_TRY(check(target));
} else if ((insn & 0x9F000000u) == 0x10000000u) { /* adr */
const u64 imm = (static_cast<u64>((insn >> 5) & 0x7FFFFu) << 2) | ((insn >> 29) & 0x3u);
const uintptr_t target = site + SignExtend(imm, 21);
R_TRY(check(target));
} else if ((insn & 0x9F000000u) == 0x90000000u) { /* adrp */
/* ADRP is page-relative and the cave is at an arbitrary offset, any adrp would point to potential garbage. */
R_THROW(ldr::ResultHookPayloadEscapes());
}
}
/* No return found. */
R_THROW(ldr::ResultHookPayloadEscapes());
}
private:
u32 *AllocCode(size_t words) {
const size_t bytes = words * sizeof(u32);
if (!this->IsEnabled() || m_used + bytes > m_cave_size) {
return nullptr;
}
u32 *p = reinterpret_cast<u32 *>(m_cave + m_used);
m_used += bytes;
return p;
}
};
inline HookContext &Hooks() {
static HookContext s_context;
return s_context;
}
}
/* Hook custom impl. Original becomes unreachable. */
/* Starts at function entry. Preserves arguments. */
#define INSTALL_IMPL_HOOK(site, fn) \
(::ams::ldr::hoc::pcv::Hooks().InstallImpl((site), reinterpret_cast<const void *>(&(fn))))
/* Maintains original function. */
/* Starts at function entry. Preserves arguments. */
#define INSTALL_IMPL_HOOK_ORIG(site, fn, out_orig) \
(::ams::ldr::hoc::pcv::Hooks().InstallImpl((site), reinterpret_cast<const void *>(&(fn)), (out_orig)))
/* Takes the same arguments as the hooked function, does not replace it and cannot change what it does */
/* but it can be placed anywhere, not just at a function entry. */
#define INSTALL_INTERC_HOOK(site, fn) \
(::ams::ldr::hoc::pcv::Hooks().InstallIntercept((site), reinterpret_cast<const void *>(&(fn))))
@@ -35,7 +35,7 @@ namespace ams::ldr::hoc::pcv::mariko {
}
}
/* Default value is 800mV. */
/* Default value is 1050mV. */
if (C.marikoGpuVmax) {
PATCH_OFFSET(ptr + 1, C.marikoGpuVmax);
}
@@ -488,7 +488,27 @@ namespace ams::ldr::hoc::pcv::mariko {
da_covers |= (w_cover << 16);
table->burst_mc_regs.mc_emem_arb_da_covers = da_covers;
table->burst_mc_regs.mc_emem_arb_misc0 = (table->burst_mc_regs.mc_emem_arb_misc0 & 0xFFE08000) | (table->burst_mc_regs.mc_emem_arb_timing_rc + 1);
constexpr u32 AtomsPerDvfsPulse = 0x7;
constexpr u32 McEmcSameFreq = 0x0;
const u32 expiringSoonSlackThreshold = [&] {
switch (table->rate_khz) {
case 2966000:
case 3100000:
case 3133000:
case 3200000:
return 0x12u;
default:
return 0x13u;
}
}();
const u32 priorityInversionIsoThreshold = GET_CYCLE_CEIL(7.5);
constexpr u32 EmcReqB2bXfer = 0x0;
const u32 priorityInversionThreshold = GET_CYCLE_CEIL(22.5);
const u32 bc2aaHoldoffThreshold = table->burst_mc_regs.mc_emem_arb_timing_rc + 1;
const u32 mc_emem_arb_misc0 = (AtomsPerDvfsPulse << 28) | (McEmcSameFreq << 27) | (expiringSoonSlackThreshold << 21) | (priorityInversionIsoThreshold << 16) | (EmcReqB2bXfer << 15) | (priorityInversionThreshold << 8) | (bc2aaHoldoffThreshold << 0);
table->burst_mc_regs.mc_emem_arb_misc0 = mc_emem_arb_misc0;
table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = 0x115;
@@ -530,11 +550,13 @@ namespace ams::ldr::hoc::pcv::mariko {
table->la_scale_regs.mc_latency_allowance_hc_1 = (table->la_scale_regs.mc_latency_allowance_hc_1 & Mask2) | allowance1;
table->la_scale_regs.mc_latency_allowance_vi2_0 = (table->la_scale_regs.mc_latency_allowance_vi2_0 & Mask2) | allowance1;
table->dram_timings.t_rp = tRFCpb;
table->dram_timings.t_rfc = tRFCab;
table->dram_timings.t_rp = tRP_values[0];
const u32 tRFCabStock = tRFC_values[0] * 2;
table->dram_timings.t_rfc = tRFCabStock;
table->dram_timings.rl = RL;
table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast<u32>(mrw2);
table->emc_mrw = (table->emc_mrw & ~0x70u) | 0x40; /* nWR */
table->emc_cfg_2 = 0x11083D;
}
@@ -876,6 +898,8 @@ namespace ams::ldr::hoc::pcv::mariko {
{ 3133000, { DVB_OC(1025, 1000, 975, 23) }, },
{ 3166000, { DVB_OC(1037, 1012, 987, 24) }, },
{ 3200000, { DVB_OC(1050, 1025, 1000, 25) }, },
{ 3266000, { DVB_OC(1075, 1050, 1025, 26) }, },
{ 3333000, { DVB_OC(1100, 1075, 1050, 27) }, },
{ ~0u, { }, },
};
#undef DVB
@@ -919,14 +943,11 @@ namespace ams::ldr::hoc::pcv::mariko {
} __attribute__((packed)) cmd;
I2cSession _session;
Result res = i2cOpenSession(&_session, dev);
if (R_FAILED(res)) {
return res;
}
R_TRY(i2cOpenSession(&_session, dev));
cmd.reg = reg;
cmd.val = val;
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
cmd.reg = reg;
cmd.val = val;
Result res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
i2csessionClose(&_session);
return res;
}
@@ -963,10 +984,6 @@ namespace ams::ldr::hoc::pcv::mariko {
Result resultI2C = I2cSet_U8(I2cDevice_Max77812_2, 0x25, (emc_uv - uv_min) / uv_step);
i2cExit();
if (R_SUCCEEDED(resultI2C)) {
R_SUCCEED();
}
return resultI2C;
}
@@ -1158,7 +1175,7 @@ namespace ams::ldr::hoc::pcv::mariko {
}
for (auto &entry : patches) {
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
LOGGING("%s Count: %zu\n", entry.description, entry.patched_count);
if (R_FAILED(entry.CheckResult())) {
panic::SmcError(panic::Patch);
@@ -60,7 +60,7 @@ namespace ams::ldr::hoc::pcv::mariko {
static const u32 cpuVoltThermalData[] = { 620, 1120, 20000, 620, 1120, 70000, 950, 1132, 0, 950, 1227, 0 };
static const u32 allowedCpuMaxFrequencies[] = { 1'963'500, 2'091'000, 2'193'000, 2'295'000, 2'397'000, 2'499'000, 2'601'000, 2'703'000, };
static const u32 allowedCpuMaxFrequencies[] = { 1'963'500, 2'091'000, 2'193'000, 2'295'000, 2'397'000, 2'499'000, 2'601'000, 2'703'000, 2'805'000 };
constexpr cvb_entry_t GpuCvbTableDefault[] = {
// GPUB01_NA_CVB_TABLE
+190 -134
View File
@@ -8,7 +8,6 @@
*/
#include <math.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -24,110 +23,178 @@
#define ALIGN_PADDING 0x100000
#define CACHE_LINE_SIZE 128
struct f_data {
void (*func)(int64_t *, int64_t *, int);
int64_t *arg1;
int64_t *arg2;
int arg3;
};
#define BW_MAX_THREADS 4
static pthread_cond_t p_ready, p_start;
static pthread_mutex_t p_lock;
static pthread_t *p_worker = NULL;
static struct f_data *worker_data = NULL;
static int p_worker_not_ready, p_workers_ready;
typedef void (*bench_kernel_fn)(int64_t *dst, int64_t *src, long size);
static void *thread_func(void *data) {
struct f_data *d = data;
pthread_mutex_lock(&p_lock);
p_worker_not_ready--;
if (!p_worker_not_ready)
pthread_cond_signal(&p_ready);
while (p_workers_ready != 1)
pthread_cond_wait(&p_start, &p_lock);
pthread_mutex_unlock(&p_lock);
(d->func)(d->arg1, d->arg2, d->arg3);
pthread_exit(NULL);
struct bw_pool;
typedef struct {
Thread thread;
Semaphore start;
struct bw_pool *pool;
bench_kernel_fn func;
int64_t *dst;
int64_t *src;
long size;
int core;
bool created;
uint64_t ticks;
} bw_worker_t;
typedef struct bw_pool {
bw_worker_t workers[BW_MAX_THREADS];
Semaphore done;
int threads;
bool ok;
} bw_pool_t;
static void bw_worker_entry(void *arg) {
bw_worker_t *w = (bw_worker_t *)arg;
bw_pool_t *p = w->pool;
/* Best-effort pin to own core. */
svcSetThreadCoreMask(CUR_THREAD_HANDLE, w->core, (u64)1 << w->core);
for (;;) {
semaphoreWait(&w->start);
bench_kernel_fn f = w->func;
if (!f)
break;
uint64_t t0 = armGetSystemTick();
f(w->dst, w->src, w->size);
w->ticks = armGetSystemTick() - t0;
semaphoreSignal(&p->done);
}
}
static void parallel_run(void) {
pthread_mutex_lock(&p_lock);
p_workers_ready = 1;
pthread_mutex_unlock(&p_lock);
pthread_cond_broadcast(&p_start);
static bool bw_pool_init(bw_pool_t *p, int threads) {
memset(p, 0, sizeof(*p));
if (threads > BW_MAX_THREADS)
threads = BW_MAX_THREADS;
p->threads = threads;
semaphoreInit(&p->done, 0);
s32 prio = 0x2C;
svcGetThreadPriority(&prio, CUR_THREAD_HANDLE);
for (int i = 0; i < threads; i++) {
bw_worker_t *w = &p->workers[i];
w->core = i;
w->func = NULL;
w->pool = p;
semaphoreInit(&w->start, 0);
if (R_FAILED(threadCreate(&w->thread, bw_worker_entry, w, NULL, 0x8000, prio, i)))
return false;
w->created = true;
if (R_FAILED(threadStart(&w->thread)))
return false;
}
p->ok = true;
return true;
}
static void parallel_init(int threads) {
pthread_attr_t attr;
pthread_cond_init(&p_ready, NULL);
pthread_cond_init(&p_start, NULL);
pthread_mutex_init(&p_lock, NULL);
p_worker_not_ready = threads;
p_workers_ready = 0;
pthread_attr_init(&attr);
if (!p_worker || !worker_data) {
p_worker = malloc(threads * sizeof(pthread_t));
worker_data = malloc(threads * sizeof(struct f_data));
static void bw_pool_free(bw_pool_t *p) {
for (int i = 0; i < p->threads; i++) {
bw_worker_t *w = &p->workers[i];
if (w->created) {
w->func = NULL; /* shutdown */
semaphoreSignal(&w->start);
}
}
for (int i = 0; i < p->threads; i++) {
bw_worker_t *w = &p->workers[i];
if (w->created) {
threadWaitForExit(&w->thread);
threadClose(&w->thread);
w->created = false;
}
}
p->ok = false;
}
static double bw_run_once(bw_pool_t *p, bench_kernel_fn f, int64_t *dstbuf, int64_t *srcbuf, long size) {
int threads = p->threads;
for (int i = 0; i < threads; i++) {
bw_worker_t *w = &p->workers[i];
w->func = f;
w->dst = dstbuf + (size * i) / (long)sizeof(int64_t);
w->src = srcbuf + (size * i) / (long)sizeof(int64_t);
w->size = size;
}
for (int i = 0; i < threads; i++)
pthread_create(p_worker + i, &attr, thread_func, worker_data + i);
pthread_mutex_lock(&p_lock);
while (p_worker_not_ready != 0)
pthread_cond_wait(&p_ready, &p_lock);
pthread_mutex_unlock(&p_lock);
semaphoreSignal(&p->workers[i].start);
for (int i = 0; i < threads; i++)
semaphoreWait(&p->done);
uint64_t sum = 0;
for (int i = 0; i < threads; i++)
sum += p->workers[i].ticks;
return (double)armTicksToNs(sum) / 1000000000.0 / threads;
}
static void aligned_block_copy(int64_t *__restrict dst_, int64_t *__restrict src, int size) {
volatile int64_t *dst = dst_;
int64_t t1, t2, t3, t4;
while ((size -= 64) >= 0) {
t1 = *src++;
t2 = *src++;
t3 = *src++;
t4 = *src++;
*dst++ = t1;
*dst++ = t2;
*dst++ = t3;
*dst++ = t4;
t1 = *src++;
t2 = *src++;
t3 = *src++;
t4 = *src++;
*dst++ = t1;
*dst++ = t2;
*dst++ = t3;
*dst++ = t4;
}
}
#define BW_GPR_CLOBBERS \
"x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18"
static void aligned_block_fetch(int64_t *__restrict dst, int64_t *__restrict src_, int size) {
volatile int64_t *src = src_;
(void)dst;
while ((size -= 64) >= 0) {
*src++;
*src++;
*src++;
*src++;
*src++;
*src++;
*src++;
*src++;
}
static void aligned_block_copy(int64_t *dst, int64_t *src, long size) {
__asm__ __volatile__(
"1:\n"
"ldp x3, x4, [%[s], #0x00]\n"
"ldp x5, x6, [%[s], #0x10]\n"
"ldp x7, x8, [%[s], #0x20]\n"
"ldp x9, x10, [%[s], #0x30]\n"
"ldp x11, x12, [%[s], #0x40]\n"
"ldp x13, x14, [%[s], #0x50]\n"
"ldp x15, x16, [%[s], #0x60]\n"
"ldp x17, x18, [%[s], #0x70]\n"
"add %[s], %[s], #0x80\n"
"stp x3, x4, [%[d], #0x00]\n"
"stp x5, x6, [%[d], #0x10]\n"
"stp x7, x8, [%[d], #0x20]\n"
"stp x9, x10, [%[d], #0x30]\n"
"stp x11, x12, [%[d], #0x40]\n"
"stp x13, x14, [%[d], #0x50]\n"
"stp x15, x16, [%[d], #0x60]\n"
"stp x17, x18, [%[d], #0x70]\n"
"add %[d], %[d], #0x80\n"
"subs %[n], %[n], #0x80\n"
"b.gt 1b\n"
: [s] "+r"(src), [d] "+r"(dst), [n] "+r"(size)
:
: BW_GPR_CLOBBERS, "cc", "memory");
}
static void aligned_block_fill(int64_t *__restrict dst_, int64_t *__restrict src, int size) {
volatile int64_t *dst = dst_;
int64_t data = *src;
while ((size -= 64) >= 0) {
*dst++ = data;
*dst++ = data;
*dst++ = data;
*dst++ = data;
*dst++ = data;
*dst++ = data;
*dst++ = data;
*dst++ = data;
}
static void aligned_block_fetch(int64_t *dst, int64_t *src, long size) {
(void)src;
__asm__ __volatile__(
"1:\n"
"ldp x3, x4, [%[p], #0x00]\n"
"ldp x5, x6, [%[p], #0x10]\n"
"ldp x7, x8, [%[p], #0x20]\n"
"ldp x9, x10, [%[p], #0x30]\n"
"ldp x11, x12, [%[p], #0x40]\n"
"ldp x13, x14, [%[p], #0x50]\n"
"ldp x15, x16, [%[p], #0x60]\n"
"ldp x17, x18, [%[p], #0x70]\n"
"add %[p], %[p], #0x80\n"
"subs %[n], %[n], #0x80\n"
"b.gt 1b\n"
: [p] "+r"(dst), [n] "+r"(size)
:
: BW_GPR_CLOBBERS, "cc", "memory");
}
static void aligned_block_fill(int64_t *dst, int64_t *src, long size) {
(void)src;
__asm__ __volatile__(
"1:\n"
"stp x3, x4, [%[d], #0x00]\n"
"stp x5, x6, [%[d], #0x10]\n"
"stp x7, x8, [%[d], #0x20]\n"
"stp x9, x10, [%[d], #0x30]\n"
"stp x11, x12, [%[d], #0x40]\n"
"stp x13, x14, [%[d], #0x50]\n"
"stp x15, x16, [%[d], #0x60]\n"
"stp x17, x18, [%[d], #0x70]\n"
"add %[d], %[d], #0x80\n"
"subs %[n], %[n], #0x80\n"
"b.gt 1b\n"
: [d] "+r"(dst), [n] "+r"(size)
:
: BW_GPR_CLOBBERS, "cc", "memory");
}
static double gettime(void) {
@@ -136,47 +203,29 @@ static double gettime(void) {
return (double)((int64_t)tv.tv_sec * 1000000 + tv.tv_usec) / 1000000.;
}
static double bandwidth_bench_helper(int threads, int64_t *dstbuf, int64_t *srcbuf, int size, void (*f)(int64_t *, int64_t *, int)) {
static double bandwidth_bench_helper(bw_pool_t *pool, int threads, int64_t *dstbuf, int64_t *srcbuf, long size, bench_kernel_fn f) {
int i, loopcount, innerloopcount, n;
double t, t1, t2, speed, maxspeed, s, s0, s1, s2;
double t, speed, maxspeed;
if (!pool || !pool->ok)
return 0.;
s = s0 = s1 = s2 = 0.;
maxspeed = 0.;
for (n = 0; n < MAXREPEATS; n++) {
bw_run_once(pool, f, dstbuf, srcbuf, size);
loopcount = 0;
innerloopcount = 1;
t = 0.;
do {
loopcount += innerloopcount;
for (i = 0; i < innerloopcount; i++) {
parallel_init(threads);
for (int pt = 0; pt < threads; pt++) {
(worker_data + pt)->func = f;
(worker_data + pt)->arg1 = dstbuf + size * pt / sizeof(int64_t);
(worker_data + pt)->arg2 = srcbuf + size * pt / sizeof(int64_t);
(worker_data + pt)->arg3 = size;
}
t1 = gettime();
parallel_run();
for (int pt = 0; pt < threads; pt++)
pthread_join(p_worker[pt], NULL);
t2 = gettime();
t += t2 - t1;
}
for (i = 0; i < innerloopcount; i++)
t += bw_run_once(pool, f, dstbuf, srcbuf, size);
innerloopcount *= 2;
} while (t < 0.5);
} while (t < 0.25);
speed = (double)size * threads * loopcount / t / 1000000.;
s0 += 1.;
s1 += speed;
s2 += speed * speed;
if (speed > maxspeed)
maxspeed = speed;
if (s0 > 2.) {
s = sqrt((s0 * s2 - s1 * s1) / (s0 * (s0 - 1)));
if (s < maxspeed / 1000.)
break;
}
}
return maxspeed;
}
@@ -306,13 +355,16 @@ void bench_run_full(bench_results_t *out, bench_progress_fn progress, void *user
int64_t *srcbuf, *dstbuf;
void *poolbuf = alloc_nonaliased_buffers((void **)&srcbuf, size * threads, (void **)&dstbuf, size * threads, NULL, 0);
bw_pool_t bw;
bw_pool_init(&bw, threads);
STEP("CPU copy", 0.40f);
out->cpu_copy = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_copy);
out->cpu_copy = bandwidth_bench_helper(&bw, threads, dstbuf, srcbuf, size, aligned_block_copy);
STEP("CPU read", 0.55f);
out->cpu_read = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_fetch);
out->cpu_read = bandwidth_bench_helper(&bw, threads, dstbuf, srcbuf, size, aligned_block_fetch);
STEP("CPU write", 0.70f);
out->cpu_write = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_fill);
out->cpu_write = bandwidth_bench_helper(&bw, threads, dstbuf, srcbuf, size, aligned_block_fill);
bw_pool_free(&bw);
free(poolbuf);
STEP("Latency", 0.85f);
@@ -325,9 +377,10 @@ void bench_run_full(bench_results_t *out, bench_progress_fn progress, void *user
struct bench_ctx {
int phase;
bool is_4gb;
void *pool;
void *buf;
int64_t *src;
int64_t *dst;
bw_pool_t bw;
};
bench_ctx *bench_begin(void) {
@@ -335,7 +388,8 @@ bench_ctx *bench_begin(void) {
if (!c)
return NULL;
c->is_4gb = (appletGetAppletType() == AppletType_Application);
c->pool = alloc_nonaliased_buffers((void **)&c->src, SIZE * 3, (void **)&c->dst, SIZE * 3, NULL, 0);
c->buf = alloc_nonaliased_buffers((void **)&c->src, SIZE * 3, (void **)&c->dst, SIZE * 3, NULL, 0);
bw_pool_init(&c->bw, 3);
return c;
}
@@ -349,24 +403,25 @@ bool bench_step(bench_ctx *c, bench_results_t *out, const char **label, float *f
*frac = 0.25f;
break;
case 1:
out->cpu_copy = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_copy);
out->cpu_copy = bandwidth_bench_helper(&c->bw, threads, c->dst, c->src, size, aligned_block_copy);
*label = "CPU copy";
*frac = 0.45f;
break;
case 2:
out->cpu_read = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_fetch);
out->cpu_read = bandwidth_bench_helper(&c->bw, threads, c->dst, c->src, size, aligned_block_fetch);
*label = "CPU read";
*frac = 0.60f;
break;
case 3:
out->cpu_write = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_fill);
out->cpu_write = bandwidth_bench_helper(&c->bw, threads, c->dst, c->src, size, aligned_block_fill);
*label = "CPU write";
*frac = 0.75f;
break;
case 4:
if (c->pool) {
free(c->pool);
c->pool = NULL;
bw_pool_free(&c->bw);
if (c->buf) {
free(c->buf);
c->buf = NULL;
}
latency_bench(&out->l2_ns, &out->ram_ns);
*label = "Latency";
@@ -384,7 +439,8 @@ bool bench_step(bench_ctx *c, bench_results_t *out, const char **label, float *f
void bench_end(bench_ctx *c) {
if (!c)
return;
if (c->pool)
free(c->pool);
bw_pool_free(&c->bw);
if (c->buf)
free(c->buf);
free(c);
}
}
@@ -0,0 +1,9 @@
Thanks to NaGa for Status Monitor Pro! <br> <br>
**Horizon-OC-Monitor is deprecated.** If you want to compile it anyway, create a `lib` folder and clone Atmosphere-libs and libultrahand into it: <br> <br>
```bash
mkdir lib
git clone https://github.com/Atmosphere-NX/Atmosphere-libs lib/Atmosphere-libs
git clone https://github.com/ppkantorski/libultrahand lib/libultrahand
```
-6
View File
@@ -1,6 +0,0 @@
[submodule "lib/Atmosphere-libs"]
path = lib/Atmosphere-libs
url = https://github.com/Atmosphere-NX/Atmosphere-libs
[submodule "lib/libultrahand"]
path = lib/libultrahand
url = https://github.com/ppkantorski/libultrahand
-1
View File
@@ -1 +0,0 @@
Thanks to NaGa for Status Monitor Pro!
@@ -0,0 +1,960 @@
/*
* 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/NvServices check cache */
bool g_is_pcv;
bool g_is_ptm;
bool g_is_nvsrv;
/* 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;
g_is_nvsrv = meta->aci->program_id == ncm::SystemProgramId::NvServices;
/* 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);
/* Reserve hook arena memory past pcv's bss. */
if (g_is_pcv && i == ctx.main_nso_idx) {
out->nso_size[i] = util::AlignUp(out->nso_size[i], os::MemoryPageSize) + hoc::pcv::PcvDataArenaSize;
}
const size_t aligned_up_size = util::AlignUp(out->nso_size[i], os::MemoryPageSize) & (AutoLoadModuleSizeMax - 1);
R_UNLESS(out->nso_size[i] <= aligned_up_size, ldr::ResultInvalidNso());
R_UNLESS(aligned_up_size > 0, ldr::ResultInvalidNso());
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;
const size_t module_size = static_cast<size_t>(nso_header->rw_dst_offset) + util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
const size_t arena_size = (nso_size > module_size) ? (nso_size - module_size) : 0;
/* Map and read data from file. */
{
/* Map the process memory. */
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) {
const size_t text_end = static_cast<size_t>(nso_header->text_size);
const size_t rx_end = util::AlignUp(text_end, os::MemoryPageSize);
const size_t ro_start = static_cast<size_t>(nso_header->ro_dst_offset);
const size_t cave_end = (rx_end < ro_start) ? rx_end : ro_start;
const uintptr_t cave = map_address + text_end;
const size_t cave_size = (cave_end > text_end) ? (cave_end - text_end) : 0;
/* module_size is used rather than nso_size to exclude the extra data section. */
hoc::pcv::Patch(map_address, module_size, cave, cave_size, nso_address, arena_size ? (map_address + module_size) : 0);
}
if (g_is_ptm) {
hoc::ptm::Patch(map_address, nso_size);
}
// TODO: figure out the cause of crashes with nvsrv patches
if (g_is_nvsrv) {
// hoc::nvsrv::Patch(map_address, nso_size, nso_address);
}
}
/* 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) + arena_size;
if (text_size) {
const bool prevent_code_reads = (nso_header->flags & NsoHeader::Flag_PreventCodeReads);
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute));
}
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();
}
}
Regular → Executable
+2 -1
View File
@@ -40,5 +40,6 @@ cp -vf "$ROOT_DIR/assets/hoc.rgba" "$DIST_DIR/config/ultrahand/assets/notificat
echo
echo "*** Copying lang ***"
cp -vr "$ROOT_DIR/overlay/lang/" "$DIST_DIR/config/horizon-oc/lang/"
mkdir -p "$DIST_DIR/config/horizon-oc/lang"
cp -vr "$ROOT_DIR/overlay/lang/." "$DIST_DIR/config/horizon-oc/lang/"
echo
@@ -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>
@@ -50,6 +50,7 @@ Result hocclkIpcSetConfigValues(HocClkConfigValueList* configValues);
Result hocclkIpcGetFreqList(HocClkModule module, u32* list, u32 maxCount, u32* outCount);
Result hocClkIpcSetKipData();
Result hocClkIpcGetKipData();
Result hocClkIpcRequestGpuVoltage(u32 voltage, u32 hz);
static inline Result hocclkIpcRemoveOverride(HocClkModule module)
{
@@ -81,9 +81,10 @@ typedef struct {
u16 resolutionHeight;
u8 custRev;
u16 kipVersion;
bool isKipLoaded;
// Reserved for future use
u8 reserved[0x35B];
u8 reserved[0x35A];
} HocClkContext;
typedef struct
+14 -8
View File
@@ -70,6 +70,7 @@ typedef enum {
HocClkConfigValue_AulaDisplayColorPreset,
HocClkConfigValue_MarikoMiddleFreqs,
HocClkConfigValue_LiveGpuVoltage,
HocClkConfigValue_AutoRAMCPUOverclock,
HocClkConfigValue_AutoRamCpuCpuOCFreq,
@@ -108,8 +109,6 @@ typedef enum {
KipConfigValue_low_t7_tWTR,
KipConfigValue_low_t8_tREFI,
KipConfigValue_t2_tRP_cap,
KipConfigValue_read_latency_1333,
KipConfigValue_read_latency_1600,
KipConfigValue_read_latency_1866,
@@ -223,6 +222,8 @@ typedef enum {
KipConfigValue_g_soc_volt_3133000,
KipConfigValue_g_soc_volt_3166000,
KipConfigValue_g_soc_volt_3200000,
KipConfigValue_g_soc_volt_3266000,
KipConfigValue_g_soc_volt_3333000,
KipConfigValue_t6_tRTW_fine_tune,
KipConfigValue_t7_tWTR_fine_tune,
@@ -315,6 +316,9 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case HocClkConfigValue_MarikoMiddleFreqs:
return pretty ? "Mariko Middle Clocks" : "mariko_middle_freqs";
case HocClkConfigValue_LiveGpuVoltage:
return pretty ? "GPU Voltage Request" : "gpu_voltage_request";
case HocClkConfigValue_AutoRAMCPUOverclock:
return pretty ? "Auto High RAM CPU OC" : "auto_high_ram_cpu_oc";
case HocClkConfigValue_AutoRamCpuCpuOCFreq:
@@ -383,10 +387,7 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case KipConfigValue_low_t7_tWTR:
return pretty ? "Low T7 - tWTR" : "low_t7_tWTR";
case KipConfigValue_low_t8_tREFI:
return pretty ? "Low T8 - tREFI" : "low_t7_tREFI";
case KipConfigValue_t2_tRP_cap:
return pretty ? "t2 - trp 1333WL Cap" : "t2_tRP_cap";
return pretty ? "Low T8 - tREFI" : "low_t8_tREFI";
case KipConfigValue_read_latency_1333:
return pretty ? "1333 Read Latency" : "read_latency_1333";
@@ -538,6 +539,8 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case KipConfigValue_g_soc_volt_3133000: return pretty ? "Mariko SOC Volt 3133 MHz" : "g_soc_volt_3133000";
case KipConfigValue_g_soc_volt_3166000: return pretty ? "Mariko SOC Volt 3166 MHz" : "g_soc_volt_3166000";
case KipConfigValue_g_soc_volt_3200000: return pretty ? "Mariko SOC Volt 3200 MHz" : "g_soc_volt_3200000";
case KipConfigValue_g_soc_volt_3266000: return pretty ? "Mariko SOC Volt 3266 MHz" : "g_soc_volt_3266000";
case KipConfigValue_g_soc_volt_3333000: return pretty ? "Mariko SOC Volt 3333 MHz" : "g_soc_volt_3333000";
case KipCrc32:
return pretty ? "CRC32" : "crc32";
@@ -568,6 +571,8 @@ static inline uint64_t hocclkDefaultConfigValue(HocClkConfigValue val)
case HocClkConfigValue_GPUSchedulingMethod:
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
case HocClkConfigValue_MarikoMiddleFreqs:
case HocClkConfigValue_LiveGpuVoltage:
case HocClkConfigValue_AutoRAMCPUOverclock:
return 0ULL;
case HocClkConfigValue_RamDisplayUnit:
return (uint64_t)RamDisplayUnit_MHz;
@@ -580,7 +585,6 @@ static inline uint64_t hocclkDefaultConfigValue(HocClkConfigValue val)
case HocClkConfigValue_ThermalThrottle:
case HocClkConfigValue_IsFirstLoad:
case HocClkConfigValue_DVFSMode:
case HocClkConfigValue_AutoRAMCPUOverclock:
return 1ULL;
case HocClkConfigValue_AutoRamCpuCpuOCFreq:
return 1683000ULL;
@@ -625,6 +629,7 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case HocClkConfigValue_LiveCpuUv:
case HocClkConfigValue_GPUSchedulingMethod:
case HocClkConfigValue_MarikoMiddleFreqs:
case HocClkConfigValue_LiveGpuVoltage:
case HocClkConfigValue_AutoRAMCPUOverclock:
return (input & 0x1) == input;
@@ -656,7 +661,6 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case KipConfigValue_low_t6_tRTW:
case KipConfigValue_low_t7_tWTR:
case KipConfigValue_low_t8_tREFI:
case KipConfigValue_t2_tRP_cap:
case KipConfigValue_read_latency_1333:
case KipConfigValue_read_latency_1600:
case KipConfigValue_read_latency_1866:
@@ -759,6 +763,8 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case KipConfigValue_g_soc_volt_3133000:
case KipConfigValue_g_soc_volt_3166000:
case KipConfigValue_g_soc_volt_3200000:
case KipConfigValue_g_soc_volt_3266000:
case KipConfigValue_g_soc_volt_3333000:
case KipConfigValue_eristaCpuVmin:
case KipConfigValue_eristaCpuUnlock:
case KipConfigValue_t6_tRTW_fine_tune:
+9 -4
View File
@@ -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>
@@ -50,9 +50,9 @@ enum HocClkIpcCmd
HocClkIpcCmd_GetFreqList = 11,
HocClkIpcCmd_SetKipData = 12,
HocClkIpcCmd_GetKipData = 13,
HocClkIpcCmd_RequestGpuVoltage = 14,
};
typedef struct
{
uint64_t tid;
@@ -69,4 +69,9 @@ typedef struct
{
HocClkModule module;
uint32_t maxCount;
} HocClkIpc_GetFreqList_Args;
} HocClkIpc_GetFreqList_Args;
typedef struct {
u32 voltage;
u32 hz;
} HocClkIpc_RequestGpuVoltage_Args;
+12 -3
View File
@@ -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>
@@ -166,4 +166,13 @@ Result hocClkIpcGetKipData()
{
u32 temp = 0;
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_GetKipData, temp);
}
}
Result hocClkIpcRequestGpuVoltage(u32 voltage, u32 hz) {
const HocClkIpc_RequestGpuVoltage_Args args = {
.voltage = voltage,
.hz = hz,
};
return serviceDispatchIn(&g_hocclkSrv, HocClkIpcCmd_RequestGpuVoltage, args);
}
+3 -3
View File
@@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk
# version control constants
#---------------------------------------------------------------------------------
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
APP_VERSION := 2.4.2 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this
APP_VERSION := 2.6.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this
TARGET_VERSION := $(APP_VERSION)
#---------------------------------------------------------------------------------
@@ -49,7 +49,7 @@ DEFINES := -DDISABLE_IPC -DTARGET="\"$(TARGET)\"" -DTARGET_VERSION="\"$(TARGET_V
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -O2 -Wall -flto -fdata-sections -ffunction-sections -fno-rtti -fno-common \
CFLAGS := -Os -Wall -flto -fdata-sections -ffunction-sections -fno-rtti -fno-common -fno-asynchronous-unwind-tables -fno-unwind-tables \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
@@ -67,7 +67,7 @@ CFLAGS += -DUI_OVERRIDE_PATH="\"$(UI_OVERRIDE_PATH)\""
CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++23
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map),-wrap,__cxa_throw -Wl,-wrap,_Unwind_Resume -Wl,-wrap,__gxx_personality_v0
LIBS := -lcurl -lz -lminizip -lmbedtls -lmbedx509 -lmbedcrypto -ljansson -lnx
+15 -1
View File
@@ -211,5 +211,19 @@
"X: %u Y: %u": "X: %u Y: %u",
"%u.%u%u mV": "%u.%u%u mV",
"Compiling with minimal features": "Kompilieren mit minimalen Funktionen",
"THE BEER-WARE LICENSE": "DIE BIERWAREN-LIZENZ"
"THE BEER-WARE LICENSE": "DIE BIERWAREN-LIZENZ",
"Horizon OC\nKip opening failed": "Horizon OC\nKIP konnte nicht geöffnet werden",
"Horizon OC\nKip read failed": "Horizon OC\nKIP konnte nicht gelesen werden",
"Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nVeraltetes KIP erkannt!\nBitte Horizon OC aktualisieren",
"Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nVeraltetes Sysmodule erkannt!\nBitte Horizon OC aktualisieren",
"Horizon OC\nKip write failed": "Horizon OC\nKIP-Schreiben fehlgeschlagen",
"Horizon OC\nKip config set failed": "Horizon OC\nKIP-Konfiguration fehlgeschlagen",
"Kip is not loaded!": "KIP ist nicht geladen!",
"Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP wurde aktualisiert\nBitte starte die Konsole neu",
"Horizon OC has been installed": "Horizon OC wurde installiert",
"Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nKonfigurationspuffer stimmt nicht überein",
"Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFrequenz deaktiviert.\nNeustart zum Anwenden.",
"Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmon-Lesen fehlgeschlagen!\n Dies kann ein Hardwareproblem sein!",
"Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nI2C-Schreiben fehlgeschlagen\nbeim Setzen von VDDQ",
"Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKIP-Versionskonflikt\nBitte Horizon OC neu installieren"
}
+15 -1
View File
@@ -137,5 +137,19 @@
"or 1228MHz on HiOPT can cause ": "or 1228MHz on HiOPT can cause ",
"permanent damage to your Switch!": "permanent damage to your Switch!",
"921MHz without UV and 960MHz on": "921MHz without UV and 960MHz on",
"SLT or HiOPT can cause ": "SLT or HiOPT can cause "
"SLT or HiOPT can cause ": "SLT or HiOPT can cause ",
"Horizon OC\nKip opening failed": "Horizon OC\nKip opening failed",
"Horizon OC\nKip read failed": "Horizon OC\nKip read failed",
"Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC",
"Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC",
"Horizon OC\nKip write failed": "Horizon OC\nKip write failed",
"Horizon OC\nKip config set failed": "Horizon OC\nKip config set failed",
"Kip is not loaded!": "Kip is not loaded!",
"Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP has been updated\nPlease reboot your console",
"Horizon OC has been installed": "Horizon OC has been installed",
"Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nConfig Buffer Mismatch",
"Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nDeactivated frequency.\nReboot to apply.",
"Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmon read failed!\n This may be a hardware issue!",
"Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nFailed to write I2C\nwhile setting vddq",
"Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC"
}
+15 -1
View File
@@ -211,5 +211,19 @@
"X: %u Y: %u": "X: %u Y: %u",
"%u.%u%u mV": "%u.%u%u mV",
"Compiling with minimal features": "Compilado con funciones mínimas",
"THE BEER-WARE LICENSE": "LICENCIA BEER-WARE"
"THE BEER-WARE LICENSE": "LICENCIA BEER-WARE",
"Horizon OC\nKip opening failed": "Horizon OC\nError al abrir el KIP",
"Horizon OC\nKip read failed": "Horizon OC\nError al leer el KIP",
"Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\n¡KIP desactualizado detectado!\nActualiza Horizon OC",
"Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\n¡Sysmodule desactualizado detectado!\nActualiza Horizon OC",
"Horizon OC\nKip write failed": "Horizon OC\nError al escribir el KIP",
"Horizon OC\nKip config set failed": "Horizon OC\nError al configurar el KIP",
"Kip is not loaded!": "¡El KIP no está cargado!",
"Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nEl KIP se ha actualizado\nReinicia la consola",
"Horizon OC has been installed": "Horizon OC se ha instalado",
"Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nDesajuste del búfer de configuración",
"Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFrecuencia desactivada.\nReinicia para aplicar.",
"Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\n¡Error al leer Secmon!\n ¡Puede ser un problema de hardware!",
"Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nError al escribir I2C\nal configurar VDDQ",
"Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nVersión de KIP no coincide\nReinstala Horizon OC"
}
+15 -1
View File
@@ -211,5 +211,19 @@
"X: %u Y: %u": "X : %u Y : %u",
"%u.%u%u mV": "%u.%u%u mV",
"Compiling with minimal features": "Compilation avec fonctionnalités minimales",
"THE BEER-WARE LICENSE": "LA LICENCE BEER-WARE"
"THE BEER-WARE LICENSE": "LA LICENCE BEER-WARE",
"Horizon OC\nKip opening failed": "Horizon OC\nÉchec d'ouverture du KIP",
"Horizon OC\nKip read failed": "Horizon OC\nÉchec de lecture du KIP",
"Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nKIP obsolète détecté !\nVeuillez mettre à jour Horizon OC",
"Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nSysmodule obsolète détecté !\nVeuillez mettre à jour Horizon OC",
"Horizon OC\nKip write failed": "Horizon OC\nÉchec d'écriture du KIP",
"Horizon OC\nKip config set failed": "Horizon OC\nÉchec de configuration du KIP",
"Kip is not loaded!": "Le KIP n'est pas chargé !",
"Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nLe KIP a été mis à jour\nVeuillez redémarrer la console",
"Horizon OC has been installed": "Horizon OC a été installé",
"Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nIncohérence du tampon de configuration",
"Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFréquence désactivée.\nRedémarrez pour appliquer.",
"Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nÉchec de lecture Secmon !\n Cela peut être un problème matériel !",
"Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nÉchec d'écriture I2C\nlors du réglage de VDDQ",
"Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nVersion du KIP incompatible\nVeuillez réinstaller Horizon OC"
}
+15 -1
View File
@@ -211,5 +211,19 @@
"X: %u Y: %u": "X: %u Y: %u",
"%u.%u%u mV": "%u.%u%u mV",
"Compiling with minimal features": "Compilazione con funzionalità minime",
"THE BEER-WARE LICENSE": "THE BEER-WARE LICENSE"
"THE BEER-WARE LICENSE": "THE BEER-WARE LICENSE",
"Horizon OC\nKip opening failed": "Horizon OC\nApertura del KIP non riuscita",
"Horizon OC\nKip read failed": "Horizon OC\nLettura del KIP non riuscita",
"Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nKIP obsoleto rilevato!\nAggiorna Horizon OC",
"Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nSysmodule obsoleto rilevato!\nAggiorna Horizon OC",
"Horizon OC\nKip write failed": "Horizon OC\nScrittura del KIP non riuscita",
"Horizon OC\nKip config set failed": "Horizon OC\nConfigurazione del KIP non riuscita",
"Kip is not loaded!": "Il KIP non è caricato!",
"Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nIl KIP è stato aggiornato\nRiavvia la console",
"Horizon OC has been installed": "Horizon OC è stato installato",
"Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nBuffer di configurazione non corrispondente",
"Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFrequenza disattivata.\nRiavvia per applicare.",
"Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nLettura Secmon non riuscita!\n Potrebbe essere un problema hardware!",
"Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nScrittura I2C non riuscita\ndurante l'impostazione di VDDQ",
"Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nVersione KIP non corrispondente\nReinstalla Horizon OC"
}
+15 -1
View File
@@ -211,5 +211,19 @@
"X: %u Y: %u": "X: %u Y: %u",
"%u.%u%u mV": "%u.%u%u mV",
"Compiling with minimal features": "最小限の機能でコンパイルする",
"THE BEER-WARE LICENSE": "ビール製品ライセンス"
"THE BEER-WARE LICENSE": "ビール製品ライセンス",
"Horizon OC\nKip opening failed": "Horizon OC\nKIPを開けませんでした",
"Horizon OC\nKip read failed": "Horizon OC\nKIPの読み取りに失敗しました",
"Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\n古いKIPを検出しました!\nHorizon OCを更新してください",
"Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\n古いsysmoduleを検出しました!\nHorizon OCを更新してください",
"Horizon OC\nKip write failed": "Horizon OC\nKIPの書き込みに失敗しました",
"Horizon OC\nKip config set failed": "Horizon OC\nKIP設定に失敗しました",
"Kip is not loaded!": "KIPが読み込まれていません!",
"Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIPが更新されました\n本体を再起動してください",
"Horizon OC has been installed": "Horizon OCがインストールされました",
"Horizon OC\nConfig Buffer Mismatch": "Horizon OC\n設定バッファが一致しません",
"Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\n周波数を無効化しました。\n再起動して適用してください。",
"Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmonの読み取りに失敗しました!\n ハードウェアの問題の可能性があります!",
"Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nVDDQ設定中に\nI2C書き込みに失敗しました",
"Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKIPバージョンが一致しません\nHorizon OCを再インストールしてください"
}
Loaded 100 of 193 files, more files were not shown because too many files have changed in this diff. Show more