Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5c1f0faa2 | ||
|
|
3baa3e7b2e | ||
|
|
da7f922782 | ||
|
|
af8912321c | ||
|
|
363d1f0e3c | ||
|
|
7e4b4b5b1d | ||
|
|
b8c006c702 | ||
|
|
b7d0e3c9fa | ||
|
|
1fc9afd6d1 | ||
|
|
88ca2e8688 | ||
|
|
02bbdcdb57 | ||
|
|
139721761a | ||
|
|
88f0dda772 | ||
|
|
38ab15a243 | ||
|
|
6e4a28ee67 | ||
|
|
0cf4927c47 | ||
|
|
bfb2009dab | ||
|
|
8c54df039f | ||
|
|
9eb53f5a0b | ||
|
|
a6f1732e1d | ||
|
|
6f4b58b83e | ||
|
|
9d12aa5cb1 | ||
|
|
ff4fd37cdc | ||
|
|
618fae2532 | ||
|
|
5e9ede09a1 | ||
|
|
40968377c5 |
No files matched your search
@@ -1,10 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -5,3 +5,4 @@ Horizon OC Compilation Instructions
|
|||||||
3. Clone the Horizon OC develop branch (``git clone https://github.com/Horizon-OC/Horizon-OC.git --recurse-submodules``)
|
3. Clone the Horizon OC develop branch (``git clone https://github.com/Horizon-OC/Horizon-OC.git --recurse-submodules``)
|
||||||
4. Run ``./build.sh`` in the root directory
|
4. Run ``./build.sh`` in the root directory
|
||||||
- If you want to compile with extensions, append ``--ext``
|
- If you want to compile with extensions, append ``--ext``
|
||||||
|
- Installing ``ccache`` is optional; if it is on your PATH it is used automatically to speed up rebuilds
|
||||||
@@ -143,8 +143,7 @@ Refer to COMPILATION.md
|
|||||||
* 665
|
* 665
|
||||||
|
|
||||||
### CPU clocks (mhz)
|
### CPU clocks (mhz)
|
||||||
* 2805 → mariko absolute max, dangerous
|
* 2703 → mariko absolute max, dangerous
|
||||||
* 2703
|
|
||||||
* 2601 → unsafe
|
* 2601 → unsafe
|
||||||
* 2499
|
* 2499
|
||||||
* 2397 → mariko safe max with UV (low speedo)
|
* 2397 → mariko safe max with UV (low speedo)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
This contains a diff all modifications done to exosphere
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
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__) \
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
154a155
|
|
||||||
> { 0xF0000005, Restriction_None, SmcShowError },
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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. */
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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, },
|
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -151,9 +151,7 @@ namespace ams::secmon {
|
|||||||
HANDLER(ExceptionVectors, I2c1, UINT64_C(0x6000F000), 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(MemoryController0, ExceptionVectors, UINT64_C(0x7001C000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||||
HANDLER(MemoryController1, MemoryController0, UINT64_C(0x7001D000), 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(Sdmmc, MemoryController1, UINT64_C(0x700B0000), 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(Disp1, Sdmmc, UINT64_C(0x54200000), UINT64_C(0x3000), true, ## __VA_ARGS__) \
|
||||||
HANDLER(Dsi, Disp1, UINT64_C(0x54300000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
HANDLER(Dsi, Disp1, UINT64_C(0x54300000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||||
HANDLER(MipiCal, Dsi, UINT64_C(0x700E3000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
HANDLER(MipiCal, Dsi, UINT64_C(0x700E3000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -1,571 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <exosphere.hpp>
|
|
||||||
#include "../secmon_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,19 +99,16 @@ namespace ams::secmon::smc {
|
|||||||
#include "secmon_define_pmc_access_table.inc"
|
#include "secmon_define_pmc_access_table.inc"
|
||||||
#include "secmon_define_mc_access_table.inc"
|
#include "secmon_define_mc_access_table.inc"
|
||||||
#include "secmon_define_emc_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_rtc_pmc_access_table.inc"
|
||||||
#include "secmon_define_mc01_access_table.inc"
|
#include "secmon_define_mc01_access_table.inc"
|
||||||
|
|
||||||
constexpr const AccessTableEntry AccessTables[] = {
|
constexpr const AccessTableEntry AccessTables[] = {
|
||||||
{ PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, },
|
{ PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, },
|
||||||
{ McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, },
|
{ McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, },
|
||||||
{ EmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController.GetAddress(), EmcAccessTable::Address, EmcAccessTable::Size, },
|
{ EmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController.GetAddress(), EmcAccessTable::Address, EmcAccessTable::Size, },
|
||||||
{ Emc01AccessTable::ReducedAccessTable.data(), Emc01AccessTable::Address + MemoryRegionVirtualDeviceExternalMemoryController0.GetAddress(), Emc01AccessTable::Address + MemoryRegionPhysicalDeviceExternalMemoryController0.GetAddress(), Emc01AccessTable::Size, },
|
{ RtcPmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceRtcPmc.GetAddress(), RtcPmcAccessTable::Address, RtcPmcAccessTable::Size, },
|
||||||
{ Emc01AccessTable::ReducedAccessTable.data(), Emc01AccessTable::Address + MemoryRegionVirtualDeviceExternalMemoryController1.GetAddress(), Emc01AccessTable::Address + MemoryRegionPhysicalDeviceExternalMemoryController1.GetAddress(), Emc01AccessTable::Size, },
|
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, },
|
||||||
{ RtcPmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceRtcPmc.GetAddress(), RtcPmcAccessTable::Address, RtcPmcAccessTable::Size, },
|
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::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) {
|
constexpr bool IsAccessAllowed(const AccessTableEntry &entry, uintptr_t address) {
|
||||||
@@ -173,12 +170,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 */
|
/* 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. */
|
/* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */
|
||||||
constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress();
|
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. */
|
/* For backwards compatibility, we'll allow access to these devices on 1.0.0. */
|
||||||
// if (GetTargetFirmware() < TargetFirmware_2_0_0) {
|
if (GetTargetFirmware() < TargetFirmware_2_0_0) {
|
||||||
virtual_address = MemoryRegionVirtualDeviceMemoryController.GetAddress() + (address - MC);
|
virtual_address = MemoryRegionVirtualDeviceMemoryController.GetAddress() + (address - MC);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform the read or write, if we should. */
|
/* Perform the read or write, if we should. */
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ namespace ams::ldr {
|
|||||||
bool g_is_ptm;
|
bool g_is_ptm;
|
||||||
|
|
||||||
/* Global Zstd decompression context. */
|
/* Global Zstd decompression context. */
|
||||||
alignas(8) u8 g_zstd_dctx_workspace[util::DecompressZstdWithBicWorkBufferSizeDefault];
|
constexpr size_t ZstdDctxWorkspaceSize = 0x176E8;
|
||||||
|
alignas(8) u8 g_zstd_dctx_workspace[ZstdDctxWorkspaceSize];
|
||||||
|
|
||||||
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
|
Result ValidateProgramVersion(ncm::ProgramId program_id, u32 version) {
|
||||||
/* No version verification is done before 8.1.0. */
|
/* No version verification is done before 8.1.0. */
|
||||||
@@ -180,8 +181,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;
|
return (MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
Npdm::ProcessAddressSpace GetProcessAddressSpace(const Meta *meta) {
|
Npdm::AddressSpaceType GetAddressSpaceType(const Meta *meta) {
|
||||||
return static_cast<Npdm::ProcessAddressSpace>((meta->npdm->flags0 & Npdm::MetaFlag0_ProcessAddressSpaceMask) >> Npdm::MetaFlag0_ProcessAddressSpaceShift);
|
return static_cast<Npdm::AddressSpaceType>((meta->npdm->flags & Npdm::MetaFlag_AddressSpaceTypeMask) >> Npdm::MetaFlag_AddressSpaceTypeShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
Acid::PoolPartition GetPoolPartition(const Meta *meta) {
|
Acid::PoolPartition GetPoolPartition(const Meta *meta) {
|
||||||
@@ -224,7 +225,7 @@ namespace ams::ldr {
|
|||||||
/* Read NSO header. */
|
/* Read NSO header. */
|
||||||
size_t read_size;
|
size_t read_size;
|
||||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, g_nso_headers + ctx.nso_count, sizeof(NsoHeader)));
|
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, g_nso_headers + ctx.nso_count, sizeof(NsoHeader)));
|
||||||
R_UNLESS_LOG(read_size == sizeof(NsoHeader), ldr::ResultInvalidNso(), "[ldr] NSO header truncated!\n");
|
R_UNLESS(read_size == sizeof(NsoHeader), ldr::ResultInvalidNso());
|
||||||
|
|
||||||
/* Note nso is present. */
|
/* Note nso is present. */
|
||||||
switch (i) {
|
switch (i) {
|
||||||
@@ -263,7 +264,7 @@ namespace ams::ldr {
|
|||||||
|
|
||||||
Result CheckAutoLoad(const AutoLoadModuleContext &ctx, u32 acid_flags) {
|
Result CheckAutoLoad(const AutoLoadModuleContext &ctx, u32 acid_flags) {
|
||||||
/* We must always have a main. */
|
/* We must always have a main. */
|
||||||
R_UNLESS_LOG(ctx.ali.has_main, ldr::ResultInvalidNso(), "[ldr] Missing main!\n");
|
R_UNLESS(ctx.ali.has_main, ldr::ResultInvalidNso());
|
||||||
|
|
||||||
/* Validate flags and extents for all present NSOs. */
|
/* Validate flags and extents for all present NSOs. */
|
||||||
for (int i = 0; i < ctx.nso_count; ++i) {
|
for (int i = 0; i < ctx.nso_count; ++i) {
|
||||||
@@ -271,11 +272,11 @@ namespace ams::ldr {
|
|||||||
|
|
||||||
/* All NSOs must not be --X. */
|
/* All NSOs must not be --X. */
|
||||||
/* This is "probably" not checked on Ounce? */
|
/* This is "probably" not checked on Ounce? */
|
||||||
R_UNLESS_LOG((hdr.flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso(), "[ldr] NSO[%d] --x not allowed!\n", i);
|
R_UNLESS((hdr.flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
|
||||||
|
|
||||||
/* Zstd compression only allowed on main, and only when both rtld+sdk are present. */
|
/* 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) {
|
if (i != ctx.main_nso_idx || ctx.rtld_idx < 0 || ctx.sdk_nso_idx < 0) {
|
||||||
R_UNLESS_LOG((hdr.flags & NsoHeader::Flag_UseZbicCompression) == 0, ldr::ResultInvalidNso(), "[ldr] NSO[%d] zbic not allowed!\n", i);
|
R_UNLESS((hdr.flags & NsoHeader::Flag_UseZbicCompression) == 0, ldr::ResultInvalidNso());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NSOs must have page-aligned segments. */
|
/* NSOs must have page-aligned segments. */
|
||||||
@@ -298,13 +299,13 @@ namespace ams::ldr {
|
|||||||
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
|
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
|
||||||
if (ctx.ali.has_rtld || ctx.ali.has_sdk) {
|
if (ctx.ali.has_rtld || ctx.ali.has_sdk) {
|
||||||
/* If we have sdk we must have rtld. */
|
/* If we have sdk we must have rtld. */
|
||||||
R_UNLESS_LOG(ctx.ali.has_rtld, ldr::ResultInvalidNso(), "[ldr] Missing rtld!\n");
|
R_UNLESS(ctx.ali.has_rtld, ldr::ResultInvalidNso());
|
||||||
|
|
||||||
/* If we have rtld, we must not have browser core dll. */
|
/* If we have rtld, we must not have browser core dll. */
|
||||||
R_UNLESS_LOG(!has_browser_dll, ldr::ResultInvalidNso(), "[ldr] BrowserCoreDll must not be present!\n");
|
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
|
||||||
} else {
|
} else {
|
||||||
/* We must not have both subsdk and browser dll. */
|
/* We must not have both subsdk and browser dll. */
|
||||||
R_UNLESS_LOG(!(ctx.ali.has_subsdk && has_browser_dll), ldr::ResultInvalidNso(), "[ldr] Can't have both subsdk and BrowserCoreDll!\n");
|
R_UNLESS(!(ctx.ali.has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@@ -393,8 +394,6 @@ namespace ams::ldr {
|
|||||||
|
|
||||||
/* If the signature check fails, we need to check if this is allowable. */
|
/* If the signature check fails, we need to check if this is allowable. */
|
||||||
if (!is_signature_valid) {
|
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. */
|
/* 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(IsDevelopmentForAcidProductionCheck(), ldr::ResultInvalidNcaSignature());
|
||||||
R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature());
|
R_UNLESS(!code_verification_data.has_data, ldr::ResultInvalidNcaSignature());
|
||||||
@@ -408,56 +407,52 @@ namespace ams::ldr {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetCreateProcessParameterFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
|
Result GetCreateProcessFlags(u32 *out, const Meta *meta, const u32 ldr_flags) {
|
||||||
const u8 meta_flags0 = meta->npdm->flags0;
|
const u8 meta_flags = meta->npdm->flags;
|
||||||
const u8 meta_flags1 = meta->npdm->flags1;
|
|
||||||
|
|
||||||
u32 flags = 0;
|
u32 flags = 0;
|
||||||
|
|
||||||
/* Set Is64Bit. */
|
/* Set Is64Bit. */
|
||||||
if (meta_flags0 & Npdm::MetaFlag0_Is64BitInstruction) {
|
if (meta_flags & Npdm::MetaFlag_Is64Bit) {
|
||||||
flags |= svc::CreateProcessParameterFlag_64Bit;
|
flags |= svc::CreateProcessFlag_Is64Bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set ProcessAddressSpace. */
|
/* Set AddressSpaceType. */
|
||||||
switch (GetProcessAddressSpace(meta)) {
|
switch (GetAddressSpaceType(meta)) {
|
||||||
case Npdm::ProcessAddressSpace_32Bit:
|
case Npdm::AddressSpaceType_32Bit:
|
||||||
flags |= svc::CreateProcessParameterFlag_AddressSpace32Bit;
|
flags |= svc::CreateProcessFlag_AddressSpace32Bit;
|
||||||
break;
|
break;
|
||||||
case Npdm::ProcessAddressSpace_64Bit36:
|
case Npdm::AddressSpaceType_64BitDeprecated:
|
||||||
flags |= svc::CreateProcessParameterFlag_AddressSpace64Bit36;
|
flags |= svc::CreateProcessFlag_AddressSpace64BitDeprecated;
|
||||||
break;
|
break;
|
||||||
case Npdm::ProcessAddressSpace_32BitNoReserved:
|
case Npdm::AddressSpaceType_32BitWithoutAlias:
|
||||||
flags |= svc::CreateProcessParameterFlag_AddressSpace32BitNoReserved;
|
flags |= svc::CreateProcessFlag_AddressSpace32BitWithoutAlias;
|
||||||
break;
|
break;
|
||||||
case Npdm::ProcessAddressSpace_64Bit39:
|
case Npdm::AddressSpaceType_64Bit:
|
||||||
flags |= svc::CreateProcessParameterFlag_AddressSpace64Bit39;
|
flags |= svc::CreateProcessFlag_AddressSpace64Bit;
|
||||||
break;
|
|
||||||
case Npdm::ProcessAddressSpace_64Bit42:
|
|
||||||
flags |= svc::CreateProcessParameterFlag_AddressSpace64Bit42;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
R_THROW(ldr::ResultInvalidMeta());
|
R_THROW(ldr::ResultInvalidMeta());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set Enable Debug. */
|
/* Set Enable Debug. */
|
||||||
if (ldr_flags & CreateProcessParameterFlag_EnableJitDebug) {
|
if (ldr_flags & CreateProcessFlag_EnableDebug) {
|
||||||
flags |= svc::CreateProcessParameterFlag_EnableJitDebug;
|
flags |= svc::CreateProcessFlag_EnableDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set Enable ASLR. */
|
/* Set Enable ASLR. */
|
||||||
if (!(ldr_flags & CreateProcessParameterFlag_DisableAslr)) {
|
if (!(ldr_flags & CreateProcessFlag_DisableAslr)) {
|
||||||
flags |= svc::CreateProcessParameterFlag_EnableAslr;
|
flags |= svc::CreateProcessFlag_EnableAslr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set Is Application. */
|
/* Set Is Application. */
|
||||||
if (IsApplication(meta)) {
|
if (IsApplication(meta)) {
|
||||||
flags |= svc::CreateProcessParameterFlag_IsApplication;
|
flags |= svc::CreateProcessFlag_IsApplication;
|
||||||
|
|
||||||
/* 7.0.0+: Set OptimizeMemoryAllocation if relevant. */
|
/* 7.0.0+: Set OptimizeMemoryAllocation if relevant. */
|
||||||
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
||||||
if (meta_flags0 & Npdm::MetaFlag0_OptimizeMemoryAllocation) {
|
if (meta_flags & Npdm::MetaFlag_OptimizeMemoryAllocation) {
|
||||||
flags |= svc::CreateProcessParameterFlag_OptimizeMemoryAllocation;
|
flags |= svc::CreateProcessFlag_OptimizeMemoryAllocation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,19 +464,19 @@ namespace ams::ldr {
|
|||||||
switch (GetPoolPartition(meta)) {
|
switch (GetPoolPartition(meta)) {
|
||||||
case Acid::PoolPartition_Application:
|
case Acid::PoolPartition_Application:
|
||||||
if (IsApplet(meta)) {
|
if (IsApplet(meta)) {
|
||||||
flags |= svc::CreateProcessParameterFlag_PoolPartitionApplet;
|
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||||
} else {
|
} else {
|
||||||
flags |= svc::CreateProcessParameterFlag_PoolPartitionApplication;
|
flags |= svc::CreateProcessFlag_PoolPartitionApplication;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Acid::PoolPartition_Applet:
|
case Acid::PoolPartition_Applet:
|
||||||
flags |= svc::CreateProcessParameterFlag_PoolPartitionApplet;
|
flags |= svc::CreateProcessFlag_PoolPartitionApplet;
|
||||||
break;
|
break;
|
||||||
case Acid::PoolPartition_System:
|
case Acid::PoolPartition_System:
|
||||||
flags |= svc::CreateProcessParameterFlag_PoolPartitionSystem;
|
flags |= svc::CreateProcessFlag_PoolPartitionSystem;
|
||||||
break;
|
break;
|
||||||
case Acid::PoolPartition_SystemNonSecure:
|
case Acid::PoolPartition_SystemNonSecure:
|
||||||
flags |= svc::CreateProcessParameterFlag_PoolPartitionSystemNonSecure;
|
flags |= svc::CreateProcessFlag_PoolPartitionSystemNonSecure;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
R_THROW(ldr::ResultInvalidMeta());
|
R_THROW(ldr::ResultInvalidMeta());
|
||||||
@@ -489,23 +484,18 @@ namespace ams::ldr {
|
|||||||
} else if (hos::GetVersion() >= hos::Version_4_0_0) {
|
} else if (hos::GetVersion() >= hos::Version_4_0_0) {
|
||||||
/* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */
|
/* On 4.0.0+, the corresponding bit was simply "UseSecureMemory". */
|
||||||
if (meta->acid->flags & Acid::AcidFlag_DeprecatedUseSecureMemory) {
|
if (meta->acid->flags & Acid::AcidFlag_DeprecatedUseSecureMemory) {
|
||||||
flags |= svc::CreateProcessParameterFlag_DeprecatedUseSecureMemory;
|
flags |= svc::CreateProcessFlag_DeprecatedUseSecureMemory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 11.0.0+/meso Set Disable DAS Merge. */
|
/* 11.0.0+/meso Set Disable DAS merge. */
|
||||||
if (meta_flags0 & Npdm::MetaFlag0_DisableDeviceAddressSpaceMerge) {
|
if (meta_flags & Npdm::MetaFlag_DisableDeviceAddressSpaceMerge) {
|
||||||
flags |= svc::CreateProcessParameterFlag_DisableDeviceAddressSpaceMerge;
|
flags |= svc::CreateProcessFlag_DisableDeviceAddressSpaceMerge;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 18.0.0+/meso Set Enable Address Sanitizer . */
|
/* 18.0.0+/meso Set Alias region extra size. */
|
||||||
if (meta_flags0 & Npdm::MetaFlag0_EnableAddressSanitizer) {
|
if (meta_flags & Npdm::MetaFlag_EnableAliasRegionExtraSize) {
|
||||||
flags |= svc::CreateProcessParameterFlag_EnableAddressSanitizer;
|
flags |= svc::CreateProcessFlag_EnableAliasRegionExtraSize;
|
||||||
}
|
|
||||||
|
|
||||||
/* 23.0.0+/meso Set Enable Shadow Stack. */
|
|
||||||
if (meta_flags1 & Npdm::MetaFlag1_EnableShadowStack) {
|
|
||||||
flags |= svc::CreateProcessParameterFlag_EnableShadowStack;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*out = flags;
|
*out = flags;
|
||||||
@@ -523,7 +513,7 @@ namespace ams::ldr {
|
|||||||
out->reslimit = resource_limit;
|
out->reslimit = resource_limit;
|
||||||
|
|
||||||
/* Set flags. */
|
/* Set flags. */
|
||||||
R_TRY(GetCreateProcessParameterFlags(std::addressof(out->flags), meta, flags));
|
R_TRY(GetCreateProcessFlags(std::addressof(out->flags), meta, flags));
|
||||||
|
|
||||||
/* 3.0.0+ System Resource Size. */
|
/* 3.0.0+ System Resource Size. */
|
||||||
if (hos::GetVersion() >= hos::Version_3_0_0) {
|
if (hos::GetVersion() >= hos::Version_3_0_0) {
|
||||||
@@ -533,7 +523,7 @@ namespace ams::ldr {
|
|||||||
/* Validate system resource usage. */
|
/* Validate system resource usage. */
|
||||||
if (meta->npdm->system_resource_size) {
|
if (meta->npdm->system_resource_size) {
|
||||||
/* Process must be 64-bit. */
|
/* Process must be 64-bit. */
|
||||||
R_UNLESS((out->flags & svc::CreateProcessParameterFlag_AddressSpace64Bit39), ldr::ResultInvalidMeta());
|
R_UNLESS((out->flags & svc::CreateProcessFlag_AddressSpace64Bit), ldr::ResultInvalidMeta());
|
||||||
|
|
||||||
/* Process must be application or applet. */
|
/* Process must be application or applet. */
|
||||||
R_UNLESS(IsApplication(meta) || IsApplet(meta), ldr::ResultInvalidMeta());
|
R_UNLESS(IsApplication(meta) || IsApplet(meta), ldr::ResultInvalidMeta());
|
||||||
@@ -606,29 +596,25 @@ namespace ams::ldr {
|
|||||||
uintptr_t aslr_start = 0;
|
uintptr_t aslr_start = 0;
|
||||||
size_t aslr_size = 0;
|
size_t aslr_size = 0;
|
||||||
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
if (hos::GetVersion() >= hos::Version_2_0_0) {
|
||||||
switch (out_param->flags & svc::CreateProcessParameterFlag_AddressSpaceMask) {
|
switch (out_param->flags & svc::CreateProcessFlag_AddressSpaceMask) {
|
||||||
case svc::CreateProcessParameterFlag_AddressSpace32Bit:
|
case svc::CreateProcessFlag_AddressSpace32Bit:
|
||||||
case svc::CreateProcessParameterFlag_AddressSpace32BitNoReserved:
|
case svc::CreateProcessFlag_AddressSpace32BitWithoutAlias:
|
||||||
aslr_start = svc::AddressSmallMap32Start;
|
aslr_start = svc::AddressSmallMap32Start;
|
||||||
aslr_size = svc::AddressSmallMap32Size;
|
aslr_size = svc::AddressSmallMap32Size;
|
||||||
break;
|
break;
|
||||||
case svc::CreateProcessParameterFlag_AddressSpace64Bit36:
|
case svc::CreateProcessFlag_AddressSpace64BitDeprecated:
|
||||||
aslr_start = svc::AddressSmallMap36Start;
|
aslr_start = svc::AddressSmallMap36Start;
|
||||||
aslr_size = svc::AddressSmallMap36Size;
|
aslr_size = svc::AddressSmallMap36Size;
|
||||||
break;
|
break;
|
||||||
case svc::CreateProcessParameterFlag_AddressSpace64Bit39:
|
case svc::CreateProcessFlag_AddressSpace64Bit:
|
||||||
aslr_start = svc::AddressMap39Start;
|
aslr_start = svc::AddressMap39Start;
|
||||||
aslr_size = svc::AddressMap39Size;
|
aslr_size = svc::AddressMap39Size;
|
||||||
break;
|
break;
|
||||||
case svc::CreateProcessParameterFlag_AddressSpace64Bit42:
|
|
||||||
aslr_start = svc::AddressMap42Start;
|
|
||||||
aslr_size = svc::AddressMap42Size;
|
|
||||||
break;
|
|
||||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* On 1.0.0, only 2 address space types existed. */
|
/* On 1.0.0, only 2 address space types existed. */
|
||||||
if (out_param->flags & svc::CreateProcessParameterFlag_AddressSpace64Bit36) {
|
if (out_param->flags & svc::CreateProcessFlag_AddressSpace64BitDeprecated) {
|
||||||
aslr_start = svc::AddressSmallMap36Start;
|
aslr_start = svc::AddressSmallMap36Start;
|
||||||
aslr_size = svc::AddressSmallMap36Size;
|
aslr_size = svc::AddressSmallMap36Size;
|
||||||
} else {
|
} else {
|
||||||
@@ -641,7 +627,7 @@ namespace ams::ldr {
|
|||||||
/* Set Create Process output. */
|
/* Set Create Process output. */
|
||||||
uintptr_t aslr_slide = 0;
|
uintptr_t aslr_slide = 0;
|
||||||
size_t free_size = (aslr_size - total_size);
|
size_t free_size = (aslr_size - total_size);
|
||||||
if (out_param->flags & svc::CreateProcessParameterFlag_EnableAslr) {
|
if (out_param->flags & svc::CreateProcessFlag_EnableAslr) {
|
||||||
aslr_slide = GenerateSecureRandom(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize;
|
aslr_slide = GenerateSecureRandom(free_size / os::MemoryBlockUnitSize) * os::MemoryBlockUnitSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,13 +649,10 @@ namespace ams::ldr {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
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. */
|
/* Select read size based on compression. */
|
||||||
size_t file_size = is_compressed ? compressed_size : segment_size;
|
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. */
|
/* Validate size. */
|
||||||
R_UNLESS(file_size <= segment_size, ldr::ResultInvalidNso());
|
R_UNLESS(file_size <= segment_size, ldr::ResultInvalidNso());
|
||||||
R_UNLESS(file_size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
R_UNLESS(file_size <= std::numeric_limits<s32>::max(), ldr::ResultInvalidNso());
|
||||||
@@ -679,19 +662,19 @@ namespace ams::ldr {
|
|||||||
uintptr_t load_address = is_compressed ? map_end - compressed_size : map_base;
|
uintptr_t load_address = is_compressed ? map_end - compressed_size : map_base;
|
||||||
size_t read_size;
|
size_t read_size;
|
||||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, file_offset, reinterpret_cast<void *>(load_address), file_size));
|
R_TRY(fs::ReadFile(std::addressof(read_size), file, file_offset, reinterpret_cast<void *>(load_address), file_size));
|
||||||
R_UNLESS_LOG(read_size == file_size, ldr::ResultInvalidNso(), "[ldr] Couldn't read segment from file!\n");
|
R_UNLESS(read_size == file_size, ldr::ResultInvalidNso());
|
||||||
|
|
||||||
/* Uncompress if necessary. */
|
/* Uncompress if necessary. */
|
||||||
R_SUCCEED_IF(!is_compressed);
|
R_SUCCEED_IF(!is_compressed);
|
||||||
|
|
||||||
auto compressed_data_buf = reinterpret_cast<const void *>(load_address);
|
auto compressed_data_buf = reinterpret_cast<const void *>(load_address);
|
||||||
|
|
||||||
if (is_zbic) {
|
if (is_zstd) {
|
||||||
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);
|
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_LOG(decompressed, ldr::ResultInvalidNso(), "[ldr] Failed to decompress segment with zbic!\n");
|
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||||
} else {
|
} else {
|
||||||
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment_size, compressed_data_buf, file_size) == static_cast<int>(segment_size));
|
bool decompressed = (util::DecompressLZ4(reinterpret_cast<void *>(map_base), segment_size, compressed_data_buf, file_size) == static_cast<int>(segment_size));
|
||||||
R_UNLESS_LOG(decompressed, ldr::ResultInvalidNso(), "[ldr] Failed to decompress segment with lz4!\n");
|
R_UNLESS(decompressed, ldr::ResultInvalidNso());
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
@@ -706,12 +689,12 @@ namespace ams::ldr {
|
|||||||
crypto::GenerateSha256(hash, sizeof(hash),
|
crypto::GenerateSha256(hash, sizeof(hash),
|
||||||
reinterpret_cast<void *>(map_address + nso_header->segments[segment].dst_offset),
|
reinterpret_cast<void *>(map_address + nso_header->segments[segment].dst_offset),
|
||||||
nso_header->segments[segment].size);
|
nso_header->segments[segment].size);
|
||||||
R_UNLESS_LOG(std::memcmp(hash, nso_header->segment_hashes[segment], sizeof(hash)) == 0, ldr::ResultInvalidNso(), "[ldr] Invalid segment hash!\n");
|
R_UNLESS(std::memcmp(hash, nso_header->segment_hashes[segment], sizeof(hash)) == 0, ldr::ResultInvalidNso());
|
||||||
R_SUCCEED();
|
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) {
|
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_zbic = (nso_header->flags & NsoHeader::Flag_UseZbicCompression) != 0;
|
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 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;
|
const size_t arena_size = (nso_size > module_size) ? (nso_size - module_size) : 0;
|
||||||
@@ -728,11 +711,11 @@ namespace ams::ldr {
|
|||||||
|
|
||||||
/* Load NSO segments. */
|
/* Load NSO segments. */
|
||||||
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Text].file_offset, nso_header->text_compressed_size, nso_header->text_size,
|
R_TRY(LoadAutoLoadModuleSegment(file, nso_header->segments[NsoHeader::Segment_Text].file_offset, nso_header->text_compressed_size, nso_header->text_size,
|
||||||
(nso_header->flags & NsoHeader::Flag_CompressedText) != 0, is_zbic, map_address + nso_header->text_dst_offset, map_end));
|
(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,
|
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_zbic, map_address + nso_header->ro_dst_offset, map_end));
|
(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,
|
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_zbic, map_address + nso_header->rw_dst_offset, map_end));
|
(nso_header->flags & NsoHeader::Flag_CompressedRw) != 0, is_zstd, map_address + nso_header->rw_dst_offset, map_end));
|
||||||
|
|
||||||
/* Clear unused space to zero. */
|
/* 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 text_end = static_cast<size_t>(nso_header->text_dst_offset) + static_cast<size_t>(nso_header->text_size);
|
||||||
@@ -795,17 +778,15 @@ namespace ams::ldr {
|
|||||||
|
|
||||||
for (int i = 0; i < ctx.nso_count; i++) {
|
for (int i = 0; i < ctx.nso_count; i++) {
|
||||||
const NsoIndex nso_idx = static_cast<NsoIndex>(ctx.ali.nso_indices[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;
|
fs::FileHandle file;
|
||||||
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(nso_idx), fs::OpenMode_Read));
|
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(nso_idx), fs::OpenMode_Read));
|
||||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||||
|
|
||||||
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, ctx.headers + i,
|
const bool is_zstd = (ctx.headers[i].flags & NsoHeader::Flag_UseZbicCompression) != 0;
|
||||||
process_info->nso_address[i], process_info->nso_size[i], map_size));
|
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. */
|
/* Load arguments, if present. */
|
||||||
@@ -832,6 +813,12 @@ namespace ams::ldr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||||
|
/* Append extra .bss for 64LUT */
|
||||||
|
/* TODO: REMOVE THIS. */
|
||||||
|
if (g_is_pcv && ctx.main_nso_idx >= 0) {
|
||||||
|
g_nso_headers[ctx.main_nso_idx].bss_size += static_cast<u32>(hoc::pcv::HocPcvScratchSize);
|
||||||
|
}
|
||||||
|
|
||||||
/* Get CreateProcessParameter. */
|
/* Get CreateProcessParameter. */
|
||||||
svc::CreateProcessParameter param;
|
svc::CreateProcessParameter param;
|
||||||
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
||||||
@@ -846,7 +833,7 @@ namespace ams::ldr {
|
|||||||
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
|
/* Set the output handle, and ensure that if we fail after this point we clean it up. */
|
||||||
out->process_handle = process_handle;
|
out->process_handle = process_handle;
|
||||||
out->code_address = param.code_address;
|
out->code_address = param.code_address;
|
||||||
ON_RESULT_FAILURE { R_DISCARD(svc::CloseHandle(process_handle)); };
|
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
||||||
|
|
||||||
/* Load all auto load modules. */
|
/* Load all auto load modules. */
|
||||||
R_RETURN(LoadAutoLoadModules(out, ctx, argument));
|
R_RETURN(LoadAutoLoadModules(out, ctx, argument));
|
||||||
@@ -904,8 +891,8 @@ namespace ams::ldr {
|
|||||||
os::ProcessId process_id = os::GetProcessId(info.process_handle);
|
os::ProcessId process_id = os::GetProcessId(info.process_handle);
|
||||||
|
|
||||||
/* Register new process. */
|
/* Register new process. */
|
||||||
const auto as_type = GetProcessAddressSpace(std::addressof(meta));
|
const auto as_type = GetAddressSpaceType(std::addressof(meta));
|
||||||
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::ProcessAddressSpace_64Bit39 || as_type == Npdm::ProcessAddressSpace_64Bit36);
|
RoManager::GetInstance().RegisterProcess(pin_id, process_id, meta.aci->program_id, as_type == Npdm::AddressSpaceType_64Bit || as_type == Npdm::AddressSpaceType_64BitDeprecated);
|
||||||
|
|
||||||
/* Register all NSOs. */
|
/* Register all NSOs. */
|
||||||
for (int i = 0; i < ctx.nso_count; i++) {
|
for (int i = 0; i < ctx.nso_count; i++) {
|
||||||
@@ -916,7 +903,7 @@ namespace ams::ldr {
|
|||||||
/* If we're overriding for HBL, perform HTML document redirection. */
|
/* If we're overriding for HBL, perform HTML document redirection. */
|
||||||
if (override_status.IsHbl()) {
|
if (override_status.IsHbl()) {
|
||||||
/* Don't validate result, failure is okay. */
|
/* Don't validate result, failure is okay. */
|
||||||
R_DISCARD(RedirectHtmlDocumentPathForHbl(loc));
|
RedirectHtmlDocumentPathForHbl(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear the external code for the program. */
|
/* Clear the external code for the program. */
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#define DISABLED 0
|
#define DISABLED 0
|
||||||
#define DEACTIVATED_GPU_FREQ 2000
|
#define DEACTIVATED_GPU_FREQ 2000
|
||||||
#define GPU_MIN_MIN_VOLT 480000
|
#define GPU_MIN_MIN_VOLT 480000
|
||||||
#define CPU_MAX_MAX_VOLT 1235000
|
#define CPU_MAX_MAX_VOLT 1200000
|
||||||
|
|
||||||
namespace ams::ldr::hoc {
|
namespace ams::ldr::hoc {
|
||||||
|
|
||||||
@@ -38,11 +38,11 @@ volatile CustomizeTable C = {
|
|||||||
.commonEmcMemVolt = 1175000, /* LPDDR4(X) JEDEC Specification */
|
.commonEmcMemVolt = 1175000, /* LPDDR4(X) JEDEC Specification */
|
||||||
.eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */
|
.eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */
|
||||||
|
|
||||||
/* Available: 66MHz step rate, 100MHz step rate, 133MHz step rate and jedec. */
|
/* Available: 33MHz step rate, 66MHz step rate, 100MHz step rate, 133MHz step rate and jedec. */
|
||||||
/* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */
|
/* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */
|
||||||
.stepMode = StepMode_Jedec,
|
.stepMode = StepMode_33MHz,
|
||||||
|
|
||||||
.marikoEmcMaxClock = 2133000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
|
.marikoEmcMaxClock = 2133000, /* Requires the EMC DVFS 63-entry patches (EMC DVFS Count / EMC SoC LUT). */
|
||||||
.marikoEmcVddqVolt = 600000,
|
.marikoEmcVddqVolt = 600000,
|
||||||
|
|
||||||
.emcDvbShift = 0,
|
.emcDvbShift = 0,
|
||||||
@@ -100,14 +100,13 @@ volatile CustomizeTable C = {
|
|||||||
/* 1120mV is NVIDIA rating */
|
/* 1120mV is NVIDIA rating */
|
||||||
.marikoCpuMaxVolt = 1120,
|
.marikoCpuMaxVolt = 1120,
|
||||||
|
|
||||||
/* Supported values: 1963500, 2091000, 2193000, 2295000, 2397000, 2499000, 2601000, 2703000, 2805000. */
|
/* Supported values: 1963500, 2091000, 2193000, 2295000, 2397000, 2499000, 2601000, 2703000. */
|
||||||
/* 1963500 is official rating of T214/Mariko, fully safe. */
|
/* 1963500 is official rating of T214/Mariko, fully safe. */
|
||||||
/* 2091000-2295000 is a slight OC which should work on all units, but no guarantees. */
|
/* 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. */
|
/* 2397000 is the max safe OC for most average units with tuned undervolt. */
|
||||||
/* 2499000 should be used with caution. */
|
/* 2499000 should be used with caution. */
|
||||||
/* 2601000 exceeds pmic limit on most consoles. */
|
/* 2601000 exceeds pmic limit on most consoles. */
|
||||||
/* 2703000 is potentially dangerous and not advised. */
|
/* 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,
|
.marikoCpuMaxClock = 1963500,
|
||||||
|
|
||||||
.eristaCpuBoostClock = 1785000, /* Default boost clock */
|
.eristaCpuBoostClock = 1785000, /* Default boost clock */
|
||||||
@@ -319,7 +318,6 @@ volatile CustomizeTable C = {
|
|||||||
{ 2499000, { 1580725, -35815, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2499000, { 1580725, -35815, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2601000, { 1702903, -36675, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2601000, { 1702903, -36675, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2703000, { 1770375, -37515, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2703000, { 1770375, -37515, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2805000, { 1838820, -37707, 113, }, { CPU_MAX_MAX_VOLT, } },
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.marikoCpuDvfsTable1581Tbreak {
|
.marikoCpuDvfsTable1581Tbreak {
|
||||||
@@ -348,7 +346,6 @@ volatile CustomizeTable C = {
|
|||||||
{ 2499000, { 1736856, -35286, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2499000, { 1736856, -35286, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2601000, { 1787838, -35967, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2601000, { 1787838, -35967, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2805000, { 1908891, -37707, 113, }, { CPU_MAX_MAX_VOLT, } },
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.marikoCpuDvfsTable1683Tbreak {
|
.marikoCpuDvfsTable1683Tbreak {
|
||||||
@@ -377,7 +374,6 @@ volatile CustomizeTable C = {
|
|||||||
{ 2499000, { 1736856, -35286, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2499000, { 1736856, -35286, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2601000, { 1787838, -35967, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2601000, { 1787838, -35967, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2703000, { 1838820, -36648, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2805000, { 1908891, -37707, 113, }, { CPU_MAX_MAX_VOLT, } },
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.marikoCpuDvfsTableExtreme {
|
.marikoCpuDvfsTableExtreme {
|
||||||
@@ -406,7 +402,6 @@ volatile CustomizeTable C = {
|
|||||||
{ 2499000, { 1580725, -35815, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2499000, { 1580725, -35815, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2601000, { 1702903, -36675, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2601000, { 1702903, -36675, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2703000, { 1775375, -37515, 113, }, { CPU_MAX_MAX_VOLT, } },
|
{ 2703000, { 1775375, -37515, 113, }, { CPU_MAX_MAX_VOLT, } },
|
||||||
{ 2805000, { 1838820, -37707, 113, }, { CPU_MAX_MAX_VOLT, } },
|
|
||||||
},
|
},
|
||||||
|
|
||||||
.eristaGpuDvfsTable = {
|
.eristaGpuDvfsTable = {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define CUST_REV 7
|
#define CUST_REV 7
|
||||||
#define KIP_VERSION 260
|
#define KIP_VERSION 300
|
||||||
|
|
||||||
#include "oc_common.hpp"
|
#include "oc_common.hpp"
|
||||||
#include "pcv/pcv_common.hpp"
|
#include "pcv/pcv_common.hpp"
|
||||||
@@ -40,6 +40,7 @@ enum StepMode: u32 {
|
|||||||
StepMode_100MHz = 1,
|
StepMode_100MHz = 1,
|
||||||
StepMode_Jedec = 2,
|
StepMode_Jedec = 2,
|
||||||
StepMode_133MHz = 3,
|
StepMode_133MHz = 3,
|
||||||
|
StepMode_33MHz = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ReadLatency: u32 {
|
enum ReadLatency: u32 {
|
||||||
|
|||||||
@@ -20,12 +20,44 @@
|
|||||||
|
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include <vapours/results/results_common.hpp>
|
#include <vapours/results/results_common.hpp>
|
||||||
#define LOGGING(fmt, ...) ((void)0)
|
|
||||||
#define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); }
|
|
||||||
|
#ifndef HOC_UART_LOG
|
||||||
|
#define HOC_UART_LOG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if HOC_UART_LOG && !(defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING))
|
||||||
|
#undef HOC_UART_LOG
|
||||||
|
#define HOC_UART_LOG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HOC_PCV_NVLOG_PATCH 1
|
||||||
|
#define HOC_PCV_FORCE_VERBOSITY 1
|
||||||
|
|
||||||
#include "customize.hpp"
|
#include "customize.hpp"
|
||||||
#include "oc_log.hpp"
|
#include "oc_log.hpp"
|
||||||
|
|
||||||
|
#define HOC_IRAM_LOG 0
|
||||||
|
|
||||||
|
#if (!HOC_UART_LOG) && (defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING))
|
||||||
|
#undef HOC_IRAM_LOG
|
||||||
|
#define HOC_IRAM_LOG 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
||||||
|
|
||||||
|
#if HOC_IRAM_LOG
|
||||||
|
#define LOGGING(...) Log(__VA_ARGS__)
|
||||||
|
#elif HOC_UART_LOG
|
||||||
|
#define LOGGING(fmt, ...) AMS_LOG(fmt "\n", ##__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define LOGGING(...) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); }
|
||||||
|
|
||||||
#define PATCH_OFFSET(offset, value) \
|
#define PATCH_OFFSET(offset, value) \
|
||||||
static_assert(sizeof(__typeof__(offset)) <= sizeof(u64)); \
|
static_assert(sizeof(__typeof__(offset)) <= sizeof(u64)); \
|
||||||
*(offset) = value;
|
*(offset) = value;
|
||||||
@@ -50,13 +82,18 @@ namespace ams::ldr {
|
|||||||
R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
|
R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017);
|
R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017);
|
||||||
R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018);
|
R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018);
|
||||||
R_DEFINE_ERROR_RESULT(HookArenaOutOfMemory, 1019);
|
R_DEFINE_ERROR_RESULT(InvalidEmcDvfsCount, 1019);
|
||||||
R_DEFINE_ERROR_RESULT(HookPayloadTooLarge, 1020);
|
R_DEFINE_ERROR_RESULT(InvalidEmcSocLut, 1020);
|
||||||
R_DEFINE_ERROR_RESULT(HookRelocationUnsupported, 1021);
|
R_DEFINE_ERROR_RESULT(InvalidEmcRateList, 1021);
|
||||||
R_DEFINE_ERROR_RESULT(HookSiteInvalid, 1022);
|
R_DEFINE_ERROR_RESULT(InvalidNvLogRedirect, 1022);
|
||||||
R_DEFINE_ERROR_RESULT(HookPayloadEscapes, 1023);
|
R_DEFINE_ERROR_RESULT(InvalidBusFreqReloc, 1023);
|
||||||
R_DEFINE_ERROR_RESULT(HookDataOutOfMemory, 1024);
|
R_DEFINE_ERROR_RESULT(HookArenaOutOfMemory, 1024);
|
||||||
R_DEFINE_ERROR_RESULT(HookUnavailable, 1025);
|
R_DEFINE_ERROR_RESULT(HookPayloadTooLarge, 1025);
|
||||||
|
R_DEFINE_ERROR_RESULT(HookRelocationUnsupported, 1026);
|
||||||
|
R_DEFINE_ERROR_RESULT(HookSiteInvalid, 1027);
|
||||||
|
R_DEFINE_ERROR_RESULT(HookPayloadEscapes, 1028);
|
||||||
|
R_DEFINE_ERROR_RESULT(HookDataOutOfMemory, 1029);
|
||||||
|
R_DEFINE_ERROR_RESULT(HookUnavailable, 1030);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ams::ldr::hoc {
|
namespace ams::ldr::hoc {
|
||||||
@@ -71,6 +108,7 @@ namespace ams::ldr::hoc {
|
|||||||
patternFn pattern_search_fn = nullptr;
|
patternFn pattern_search_fn = nullptr;
|
||||||
Pointer value_search;
|
Pointer value_search;
|
||||||
size_t patched_count = 0;
|
size_t patched_count = 0;
|
||||||
|
bool optional = false;
|
||||||
|
|
||||||
Result Apply(Pointer *ptr) {
|
Result Apply(Pointer *ptr) {
|
||||||
Result res = patcher_fn(ptr);
|
Result res = patcher_fn(ptr);
|
||||||
@@ -101,7 +139,7 @@ namespace ams::ldr::hoc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result CheckResult() {
|
Result CheckResult() {
|
||||||
R_UNLESS(patched_count > 0, ldr::ResultUnsuccessfulPatcher());
|
R_UNLESS(optional || patched_count > 0, ldr::ResultUnsuccessfulPatcher());
|
||||||
|
|
||||||
if (maximum_patched_count) {
|
if (maximum_patched_count) {
|
||||||
R_UNLESS(patched_count <= maximum_patched_count, ldr::ResultUnsuccessfulPatcher());
|
R_UNLESS(patched_count <= maximum_patched_count, ldr::ResultUnsuccessfulPatcher());
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "oc_common.hpp"
|
#include "oc_common.hpp"
|
||||||
|
|
||||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
#if HOC_IRAM_LOG
|
||||||
#include "fatal_handler_bin.h"
|
#include "fatal_handler_bin.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -70,6 +70,32 @@ namespace ams::ldr::hoc {
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HOC_UART_LOG
|
||||||
|
/* Remove this? */
|
||||||
|
void UartLog(const char *fmt, ...) {
|
||||||
|
char line[256];
|
||||||
|
constexpr size_t PrefixLen = 13; /* "[HOC] " */
|
||||||
|
std::memcpy(line, "[Horizon OC] ", PrefixLen);
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
int n = vsnprintf(line + PrefixLen, sizeof(line) - PrefixLen - 1, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
if (n < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t body = static_cast<size_t>(n);
|
||||||
|
if (body > sizeof(line) - PrefixLen - 2) { /* vsnprintf returns the untruncated length */
|
||||||
|
body = sizeof(line) - PrefixLen - 2;
|
||||||
|
}
|
||||||
|
size_t len = PrefixLen + body;
|
||||||
|
line[len++] = '\n';
|
||||||
|
|
||||||
|
svc::OutputDebugString(line, len);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct log_ctx_t {
|
struct log_ctx_t {
|
||||||
u32 magic;
|
u32 magic;
|
||||||
u32 sz;
|
u32 sz;
|
||||||
@@ -81,7 +107,7 @@ namespace ams::ldr::hoc {
|
|||||||
#define IRAM_LOG_CTX_ADDR 0x4003C000
|
#define IRAM_LOG_CTX_ADDR 0x4003C000
|
||||||
#define IRAM_LOG_MAX_SZ 4096
|
#define IRAM_LOG_MAX_SZ 4096
|
||||||
|
|
||||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
#if HOC_IRAM_LOG
|
||||||
void Log(const char *data, ...) {
|
void Log(const char *data, ...) {
|
||||||
static const u32 max_log_sz = sizeof(working_buf) - sizeof(log_ctx_t);
|
static const u32 max_log_sz = sizeof(working_buf) - sizeof(log_ctx_t);
|
||||||
static bool initDone = false;
|
static bool initDone = false;
|
||||||
@@ -112,8 +138,8 @@ namespace ams::ldr::hoc {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
|
|
||||||
void ViewLog() {
|
void ViewLog() {
|
||||||
|
#if HOC_IRAM_LOG
|
||||||
if (spl::GetSocType() == spl::SocType_Mariko) {
|
if (spl::GetSocType() == spl::SocType_Mariko) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -127,6 +153,6 @@ namespace ams::ldr::hoc {
|
|||||||
SmcRebootToIramPayload();
|
SmcRebootToIramPayload();
|
||||||
|
|
||||||
while(true) { }
|
while(true) { }
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
@@ -25,4 +25,8 @@ namespace ams::ldr::hoc {
|
|||||||
void Log(const char *data, ...);
|
void Log(const char *data, ...);
|
||||||
void ViewLog();
|
void ViewLog();
|
||||||
|
|
||||||
|
/* Emit a formatted line over the kernel debug UART via svcOutputDebugString.
|
||||||
|
No-op unless the running kernel enables debug logging (audit/debug mesosphere). */
|
||||||
|
void UartLog(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||||
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -14,7 +14,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../mtc_timing_value.hpp"
|
#include "../../mtc_timing_value.hpp"
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::erista {
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
File renamed without changes.
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pcv.hpp"
|
||||||
|
#include "../../mtc_timing_value.hpp"
|
||||||
|
#include "pcv_erista_cpu.hpp"
|
||||||
|
#include "pcv_erista_gpu.hpp"
|
||||||
|
#include "pcv_erista_mtc.hpp"
|
||||||
|
#include "calculate_timings_erista.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
|
DEFINE_HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
|
||||||
|
|
||||||
|
u32 *nsoStart;
|
||||||
|
|
||||||
|
Result InstallHooks() {
|
||||||
|
R_TRY(Hooks().CheckEnabled());
|
||||||
|
|
||||||
|
R_TRY(Hooks().CopyPayload());
|
||||||
|
|
||||||
|
auto *data = Hooks().BindData(e_HookPayloadData);
|
||||||
|
R_UNLESS(data != nullptr, ldr::ResultHookDataOutOfMemory());
|
||||||
|
|
||||||
|
R_TRY(MtcInstallHooks(data));
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||||
|
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
||||||
|
MtcGenerateFreqTables();
|
||||||
|
|
||||||
|
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
||||||
|
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
|
||||||
|
|
||||||
|
PatcherEntry<u32> patches[] = {
|
||||||
|
{"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, CpuCvbDefaultMaxFreq },
|
||||||
|
{"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, CpuVminOfficial },
|
||||||
|
{"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial },
|
||||||
|
{"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low },
|
||||||
|
{"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial },
|
||||||
|
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
|
||||||
|
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq },
|
||||||
|
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
|
||||||
|
{"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
|
||||||
|
// {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
|
||||||
|
{"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn },
|
||||||
|
{"MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
|
||||||
|
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
|
||||||
|
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
|
||||||
|
{"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) {
|
||||||
|
u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
|
||||||
|
for (auto &entry : patches) {
|
||||||
|
if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &entry : patches) {
|
||||||
|
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
||||||
|
if (R_FAILED(entry.CheckResult())) {
|
||||||
|
panic::SmcError(panic::Patch);
|
||||||
|
|
||||||
|
CRASH(entry.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(InstallHooks())) {
|
||||||
|
panic::SmcError(panic::Patch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../oc_common.hpp"
|
||||||
|
#include "../pcv_common.hpp"
|
||||||
|
#include "../pcv_asm.hpp"
|
||||||
|
#include "../pcv_hook.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
|
struct HookPayloadData {
|
||||||
|
struct {
|
||||||
|
EristaMtcTable *mtcTable;
|
||||||
|
u32 mtcCount;
|
||||||
|
} mtcTableAsm;
|
||||||
|
};
|
||||||
|
DECLARE_HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
|
||||||
|
|
||||||
|
extern u32 *nsoStart;
|
||||||
|
|
||||||
|
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pcv.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
|
Result CpuVoltDvfs(u32 *ptr) {
|
||||||
|
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
|
||||||
|
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaCpuVmin) {
|
||||||
|
PATCH_OFFSET(ptr, C.eristaCpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaCpuUV) {
|
||||||
|
PATCH_OFFSET(ptr - 2, C.eristaCpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaCpuMaxVolt) {
|
||||||
|
PATCH_OFFSET(ptr + 5, C.eristaCpuMaxVolt);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CpuVoltThermals(u32 *ptr) {
|
||||||
|
if (std::memcmp(ptr - 6, cpuVoltageThermalPattern, sizeof(cpuVoltageThermalPattern))) {
|
||||||
|
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaCpuVmin) {
|
||||||
|
PATCH_OFFSET( ptr, C.eristaCpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 3, C.eristaCpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 6, C.eristaCpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaCpuMaxVolt) {
|
||||||
|
PATCH_OFFSET(ptr - 2, C.eristaCpuMaxVolt);
|
||||||
|
PATCH_OFFSET(ptr + 1, C.eristaCpuMaxVolt);
|
||||||
|
PATCH_OFFSET(ptr + 4, C.eristaCpuMaxVolt);
|
||||||
|
PATCH_OFFSET(ptr + 7, C.eristaCpuMaxVolt);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CpuVoltDfll(u32* ptr) {
|
||||||
|
CvbCpuDfllData *entry = reinterpret_cast<CvbCpuDfllData *>(ptr);
|
||||||
|
|
||||||
|
R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
|
||||||
|
if (!C.eristaCpuUV) {
|
||||||
|
R_SKIP();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (C.eristaCpuUV) {
|
||||||
|
case 1:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27007ff);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xefff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27407ff);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27807ff);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27a07ff);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xcfdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x37007ff);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "pcv_erista.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
|
constexpr cvb_entry_t CpuCvbTableDefault[] = {
|
||||||
|
// CPU_PLL_CVB_TABLE_ODN
|
||||||
|
{ 204000, {721094}, { } },
|
||||||
|
{ 306000, {754040}, { } },
|
||||||
|
{ 408000, {786986}, { } },
|
||||||
|
{ 510000, {819932}, { } },
|
||||||
|
{ 612000, {852878}, { } },
|
||||||
|
{ 714000, {885824}, { } },
|
||||||
|
{ 816000, {918770}, { } },
|
||||||
|
{ 918000, {951716}, { } },
|
||||||
|
{ 1020000, {984662}, { -2875621, 358099, -8585} },
|
||||||
|
{ 1122000, {1017608}, { -52225, 104159, -2816} },
|
||||||
|
{ 1224000, {1050554}, { 1076868, 8356, -727} },
|
||||||
|
{ 1326000, {1083500}, { 2208191, -84659, 1240} },
|
||||||
|
{ 1428000, {1116446}, { 2519460, -105063, 1611} },
|
||||||
|
{ 1581000, {1130000}, { 2889664, -122173, 1834} },
|
||||||
|
{ 1683000, {1168000}, { 5100873, -279186, 4747} },
|
||||||
|
{ 1785000, {1227500}, { 5100873, -279186, 4747} },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u32 CpuVoltOfficial = 1227;
|
||||||
|
constexpr u32 CpuVminOfficial = 825;
|
||||||
|
constexpr u32 CpuTune0Low = 0xFFEAD0FF;
|
||||||
|
|
||||||
|
constexpr u32 CpuVoltL4T = 1257'000;
|
||||||
|
|
||||||
|
static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 };
|
||||||
|
static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "Invalid cpuVoltDvfsPattern size");
|
||||||
|
|
||||||
|
static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 };
|
||||||
|
static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "Invalid cpuVoltageThermalPattern size");
|
||||||
|
|
||||||
|
|
||||||
|
Result CpuVoltDvfs(u32 *ptr);
|
||||||
|
Result CpuVoltThermals(u32 *ptr);
|
||||||
|
Result CpuVoltDfll(u32* ptr);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pcv.hpp"
|
||||||
|
#include "../pcv_asm.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
|
Result GpuVoltDVFS(u32 *ptr) {
|
||||||
|
if (std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern))) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaGpuVmin) {
|
||||||
|
PATCH_OFFSET(ptr, C.eristaGpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GpuVoltThermals(u32 *ptr) {
|
||||||
|
if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.eristaGpuVmin) {
|
||||||
|
PATCH_OFFSET(ptr, C.eristaGpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 3, C.eristaGpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 6, C.eristaGpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 9, C.eristaGpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 12, C.eristaGpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GpuFreqMaxAsm(u32 *ptr32) {
|
||||||
|
// Check if both two instructions match the pattern
|
||||||
|
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
|
||||||
|
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both instructions should operate on the same register
|
||||||
|
u8 rd = asm_get_rd(ins1);
|
||||||
|
if (rd != asm_get_rd(ins2)) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 max_clock;
|
||||||
|
switch (C.eristaGpuUV) {
|
||||||
|
case 0:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 asm_patch[2] = {
|
||||||
|
asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd),
|
||||||
|
asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)
|
||||||
|
};
|
||||||
|
|
||||||
|
PATCH_OFFSET(ptr32, asm_patch[0]);
|
||||||
|
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GpuFreqPllMax(u32 *ptr) {
|
||||||
|
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
|
||||||
|
|
||||||
|
// All zero except for freq
|
||||||
|
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) {
|
||||||
|
R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double the max clk simply
|
||||||
|
u32 max_clk = entry->freq * 2;
|
||||||
|
entry->freq = max_clk;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
// patch out 1305MHz limit on erista, don't use this!
|
||||||
|
// Result GpuFreqPllLimit(u32 *ptr) {
|
||||||
|
// u32 prev_freq = *(ptr - 1);
|
||||||
|
|
||||||
|
// if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) {
|
||||||
|
// R_THROW(ldr::ResultInvalidGpuPllEntry());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// PATCH_OFFSET(ptr, 3600000);
|
||||||
|
|
||||||
|
// R_SUCCEED();
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pcv_erista.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
|
constexpr u32 GpuClkPllLimit = 2'600'000;
|
||||||
|
constexpr u32 GpuClkPllMax = 921'600'000;
|
||||||
|
constexpr u32 GpuVminOfficial = 810;
|
||||||
|
|
||||||
|
static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, };
|
||||||
|
static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern");
|
||||||
|
|
||||||
|
static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 };
|
||||||
|
static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "Invalid gpuVoltageThermalPattern size");
|
||||||
|
|
||||||
|
/* GPU Max Clock asm Pattern:
|
||||||
|
*
|
||||||
|
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
|
||||||
|
* sf | opc | | hw | imm16 | Rd
|
||||||
|
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||||
|
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
|
||||||
|
*
|
||||||
|
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
|
||||||
|
* sf | opc | | hw | imm16 | Rd
|
||||||
|
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
||||||
|
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
|
||||||
|
*/
|
||||||
|
inline constexpr u32 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 };
|
||||||
|
|
||||||
|
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
|
||||||
|
return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr cvb_entry_t GpuCvbTableDefault[] = {
|
||||||
|
// NA_FREQ_CVB_TABLE
|
||||||
|
{ 76800, {}, { 814294, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 153600, {}, { 856185, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 230400, {}, { 898077, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 307200, {}, { 939968, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 384000, {}, { 981860, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 460800, {}, { 1023751, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 537600, {}, { 1065642, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 614400, {}, { 1107534, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 691200, {}, { 1149425, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 768000, {}, { 1191317, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 844800, {}, { 1233208, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ 921600, {}, { 1275100, 8144, -940, 808, -21583, 226, } },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
Result GpuVoltDVFS(u32 *ptr);
|
||||||
|
Result GpuVoltThermals(u32 *ptr);
|
||||||
|
Result GpuFreqMaxAsm(u32 *ptr32);
|
||||||
|
Result GpuFreqPllMax(u32 *ptr);
|
||||||
|
|
||||||
|
// patch out 1305MHz limit on erista, don't use this!
|
||||||
|
// Result GpuFreqPllLimit(u32 *ptr);
|
||||||
|
|
||||||
|
}
|
||||||
+16
-257
@@ -21,181 +21,22 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "pcv.hpp"
|
#include "../pcv.hpp"
|
||||||
#include "../mtc_timing_value.hpp"
|
#include "../../mtc_timing_value.hpp"
|
||||||
#include "../erista/calculate_timings_erista.hpp"
|
#include "calculate_timings_erista.hpp"
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::erista {
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
struct HookPayloadData {
|
|
||||||
struct {
|
|
||||||
EristaMtcTable *mtcTable;
|
|
||||||
u32 mtcCount;
|
|
||||||
} mtcTableAsm;
|
|
||||||
};
|
|
||||||
DEFINE_HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::vector<u32> newEmcList;
|
std::vector<u32> newEmcList;
|
||||||
u32 *nsoStart;
|
|
||||||
u32 *nsoEnd;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u32 *getEristaMtcTableFnSite = nullptr;
|
u32 *getEristaMtcTableFnSite = nullptr;
|
||||||
EristaMtcTable *mtcTable = nullptr;
|
EristaMtcTable *mtcTable = nullptr;
|
||||||
bool foundMtcTablePattern = false;
|
bool foundMtcTablePattern = false;
|
||||||
} getMtcTableCache;
|
} getMtcTableCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result CpuVoltDvfs(u32 *ptr) {
|
|
||||||
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
|
|
||||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaCpuVmin) {
|
|
||||||
PATCH_OFFSET(ptr, C.eristaCpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaCpuUV) {
|
|
||||||
PATCH_OFFSET(ptr - 2, C.eristaCpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaCpuMaxVolt) {
|
|
||||||
PATCH_OFFSET(ptr + 5, C.eristaCpuMaxVolt);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CpuVoltThermals(u32 *ptr) {
|
|
||||||
if (std::memcmp(ptr - 6, cpuVoltageThermalPattern, sizeof(cpuVoltageThermalPattern))) {
|
|
||||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaCpuVmin) {
|
|
||||||
PATCH_OFFSET( ptr, C.eristaCpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 3, C.eristaCpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 6, C.eristaCpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaCpuMaxVolt) {
|
|
||||||
PATCH_OFFSET(ptr - 2, C.eristaCpuMaxVolt);
|
|
||||||
PATCH_OFFSET(ptr + 1, C.eristaCpuMaxVolt);
|
|
||||||
PATCH_OFFSET(ptr + 4, C.eristaCpuMaxVolt);
|
|
||||||
PATCH_OFFSET(ptr + 7, C.eristaCpuMaxVolt);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CpuVoltDfll(u32* ptr) {
|
|
||||||
CvbCpuDfllData *entry = reinterpret_cast<CvbCpuDfllData *>(ptr);
|
|
||||||
|
|
||||||
R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
|
||||||
R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
|
||||||
R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
|
||||||
R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
|
||||||
|
|
||||||
if (!C.eristaCpuUV) {
|
|
||||||
R_SKIP();
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(C.eristaCpuUV) {
|
|
||||||
case 1:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27007ff);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xefff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27407ff);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27807ff);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27a07ff);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xcfdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x37007ff);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GpuVoltDVFS(u32 *ptr) {
|
|
||||||
if (std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern))) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaGpuVmin) {
|
|
||||||
PATCH_OFFSET(ptr, C.eristaGpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GpuVoltThermals(u32 *ptr) {
|
|
||||||
if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.eristaGpuVmin) {
|
|
||||||
PATCH_OFFSET(ptr, C.eristaGpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 3, C.eristaGpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 6, C.eristaGpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 9, C.eristaGpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 12, C.eristaGpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GpuFreqMaxAsm(u32 *ptr32) {
|
|
||||||
// Check if both two instructions match the pattern
|
|
||||||
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
|
|
||||||
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both instructions should operate on the same register
|
|
||||||
u8 rd = asm_get_rd(ins1);
|
|
||||||
if (rd != asm_get_rd(ins2)) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 max_clock;
|
|
||||||
switch (C.eristaGpuUV) {
|
|
||||||
case 0:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 asm_patch[2] = {
|
|
||||||
asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd),
|
|
||||||
asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)
|
|
||||||
};
|
|
||||||
|
|
||||||
PATCH_OFFSET(ptr32, asm_patch[0]);
|
|
||||||
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note: This does not have proper timings, so base latency adjustment will not work. */
|
/* Note: This does not have proper timings, so base latency adjustment will not work. */
|
||||||
/* However, it may still achieve a slightly higher frequency, but not as much as it could be. */
|
/* However, it may still achieve a slightly higher frequency, but not as much as it could be. */
|
||||||
/* I'm certainly not insane enough to attempt this pain again, so this will have to do *for now*. */
|
/* I'm certainly not insane enough to attempt this pain again, so this will have to do *for now*. */
|
||||||
@@ -572,33 +413,6 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GpuFreqPllMax(u32 *ptr) {
|
|
||||||
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
|
|
||||||
|
|
||||||
// All zero except for freq
|
|
||||||
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) {
|
|
||||||
R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double the max clk simply
|
|
||||||
u32 max_clk = entry->freq * 2;
|
|
||||||
entry->freq = max_clk;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
// patch out 1305MHz limit on erista, don't use this!
|
|
||||||
// Result GpuFreqPllLimit(u32 *ptr) {
|
|
||||||
// u32 prev_freq = *(ptr - 1);
|
|
||||||
|
|
||||||
// if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) {
|
|
||||||
// R_THROW(ldr::ResultInvalidGpuPllEntry());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// PATCH_OFFSET(ptr, 3600000);
|
|
||||||
|
|
||||||
// R_SUCCEED();
|
|
||||||
// }
|
|
||||||
|
|
||||||
HOOK_PAYLOAD_FN EristaMtcTable *GetEristaMtcTableImpl(u32 *count) {
|
HOOK_PAYLOAD_FN EristaMtcTable *GetEristaMtcTableImpl(u32 *count) {
|
||||||
const HookPayloadData *data = HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
|
const HookPayloadData *data = HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
|
||||||
|
|
||||||
@@ -606,6 +420,18 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
return data->mtcTableAsm.mtcTable;
|
return data->mtcTableAsm.mtcTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result MtcInstallHooks(HookPayloadData *data) {
|
||||||
|
R_UNLESS(getMtcTableCache.getEristaMtcTableFnSite != nullptr && getMtcTableCache.mtcTable != nullptr, ldr::ResultInvalidMtcTablePattern());
|
||||||
|
|
||||||
|
/* Copy the data to the payload. */
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
Result MemMtcTableAsm(u32 *ptr) {
|
Result MemMtcTableAsm(u32 *ptr) {
|
||||||
/* Return if the pattern was already found. */
|
/* Return if the pattern was already found. */
|
||||||
/* This pattern happens multiple times in this function., we only need to find it once. */
|
/* This pattern happens multiple times in this function., we only need to find it once. */
|
||||||
@@ -650,71 +476,4 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
R_SUCCEED();
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
|
||||||
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
|
||||||
nsoEnd = reinterpret_cast<u32 *>(mapped_nso + nso_size);
|
|
||||||
MtcGenerateFreqTables();
|
|
||||||
|
|
||||||
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
|
||||||
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
|
|
||||||
|
|
||||||
PatcherEntry<u32> patches[] = {
|
|
||||||
{"CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, CpuCvbDefaultMaxFreq },
|
|
||||||
{"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, CpuVminOfficial },
|
|
||||||
{"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial },
|
|
||||||
{"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low },
|
|
||||||
{"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial },
|
|
||||||
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
|
|
||||||
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq },
|
|
||||||
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
|
|
||||||
{"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
|
|
||||||
// {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
|
|
||||||
{"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn },
|
|
||||||
{"MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
|
|
||||||
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
|
|
||||||
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
|
|
||||||
{"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) {
|
|
||||||
u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
|
|
||||||
for (auto &entry : patches) {
|
|
||||||
if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &entry : patches) {
|
|
||||||
LOGGING("%s Count: %zu\n", entry.description, entry.patched_count);
|
|
||||||
if (R_FAILED(entry.CheckResult())) {
|
|
||||||
panic::SmcError(panic::Patch);
|
|
||||||
|
|
||||||
CRASH(entry.description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (R_FAILED(InstallHooks())) {
|
|
||||||
panic::SmcError(panic::Patch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pcv_erista.hpp"
|
||||||
|
#include "../../mtc_timing_table.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
|
constexpr u32 EmcListDefault[] = { 40800, 68000, 102000, 204000, 408000, 665600, 800000, 1065600, 1331200, 1600000, };
|
||||||
|
constexpr u32 EmcListSizeDefault = std::size(EmcListDefault);
|
||||||
|
constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1;
|
||||||
|
|
||||||
|
constexpr u32 MemVoltHOS = 1125'000;
|
||||||
|
constexpr u32 EmcClkPllmLimit = 1866'000'000;
|
||||||
|
|
||||||
|
constexpr u32 MTC_TABLE_REV = 7;
|
||||||
|
constexpr u32 MtcTableCountDefault = 10;
|
||||||
|
|
||||||
|
constexpr size_t MtcFullTableSize = sizeof(EristaMtcTable) * MtcTableCountDefault;
|
||||||
|
constexpr u32 MtcFullTableCount = 3;
|
||||||
|
|
||||||
|
/* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
|
||||||
|
enum DramId {
|
||||||
|
ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH = 0,
|
||||||
|
ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE = 1,
|
||||||
|
ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC = 2,
|
||||||
|
ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH = 4,
|
||||||
|
ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MtcTableIndex {
|
||||||
|
T210SdevEmcDvfsTableS4gb01 = 0, /* HB-MGCH, WT:C */
|
||||||
|
T210SdevEmcDvfsTableS6gb01 = 1, /* HM-MGCH */
|
||||||
|
T210SdevEmcDvfsTableH4gb01 = 2, /* HR-NLE */
|
||||||
|
MtcTableIndex_Invalid = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MtcDramIndex {
|
||||||
|
DramId dramId;
|
||||||
|
MtcTableIndex index;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TODO: Test 6gb and 8gb. */
|
||||||
|
const inline MtcDramIndex mtcIndexTable[] = {
|
||||||
|
{ ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, },
|
||||||
|
{ ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC, T210SdevEmcDvfsTableS4gb01, },
|
||||||
|
{ ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, },
|
||||||
|
{ ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX, T210SdevEmcDvfsTableS6gb01, },
|
||||||
|
{ ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, },
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u32 MtcBrAsm = 0xD61F0140;
|
||||||
|
constexpr u32 MtcMovAsm = 0x52800148;
|
||||||
|
constexpr u32 MtcAdrpAsm = 0xD0000081;
|
||||||
|
constexpr u32 MtcBlIns = 0x97ffae64;
|
||||||
|
constexpr u32 MtcAddAsm = 0x91131821;
|
||||||
|
|
||||||
|
ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) {
|
||||||
|
/* This builds an address that gets returned, so the register must be x0 by convention. */
|
||||||
|
return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result MemFreqMtcTable(u32 *ptr);
|
||||||
|
void MtcGenerateFreqTables();
|
||||||
|
Result MemFreqMax(u32 *ptr);
|
||||||
|
HOOK_PAYLOAD_FN EristaMtcTable *GetEristaMtcTableImpl(u32 *count);
|
||||||
|
Result MemMtcTableAsm(u32 *ptr);
|
||||||
|
|
||||||
|
Result MtcInstallHooks(HookPayloadData *data);
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "../mtc_timing_value.hpp"
|
#include "../../mtc_timing_value.hpp"
|
||||||
#include "timing_tables.hpp"
|
#include "timing_tables.hpp"
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::mariko {
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
File renamed without changes.
@@ -0,0 +1,588 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "../pcv.hpp"
|
||||||
|
#include "../../mtc_timing_value.hpp"
|
||||||
|
#include "pcv_mariko.hpp"
|
||||||
|
#include "pcv_mariko_cpu.hpp"
|
||||||
|
#include "pcv_mariko_gpu.hpp"
|
||||||
|
#include "pcv_mariko_mtc.hpp"
|
||||||
|
#include "calculate_timings_mariko.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
|
|
||||||
|
u32 *nsoStart;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
size_t g_nso_size = 0;
|
||||||
|
uintptr_t g_cave_cursor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uintptr_t CaveReserve(size_t count) {
|
||||||
|
if (g_pcv_cave == 0 || g_cave_cursor == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (g_cave_cursor + count * sizeof(u32) > g_pcv_cave + g_pcv_cave_size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const uintptr_t entry = g_cave_cursor;
|
||||||
|
g_cave_cursor += count * sizeof(u32);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if HOC_UART_LOG
|
||||||
|
/* Redirect pcv's NvLog() calls to UART */
|
||||||
|
Result NvLogUartRedirect(u32 *ptr) {
|
||||||
|
const uintptr_t mapped_nso = reinterpret_cast<uintptr_t>(nsoStart);
|
||||||
|
const size_t nso_size = g_nso_size;
|
||||||
|
const uintptr_t textEnd = g_pcv_cave; /* .text ends where the cave begins */
|
||||||
|
const uintptr_t vsnprintf_addr = reinterpret_cast<uintptr_t>(ptr);
|
||||||
|
|
||||||
|
/* NvLog via the VDD_SOC log */
|
||||||
|
static const char Fmt[] = "%s(%s): DVFS request VDD_SOC %d mV\n";
|
||||||
|
constexpr size_t FmtLen = sizeof(Fmt) - 1;
|
||||||
|
uintptr_t strAddr = 0;
|
||||||
|
{
|
||||||
|
const char *hay = reinterpret_cast<const char *>(mapped_nso);
|
||||||
|
for (size_t i = 0; i + FmtLen <= nso_size; ++i) {
|
||||||
|
if (std::memcmp(hay + i, Fmt, FmtLen) == 0) { strAddr = mapped_nso + i; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strAddr == 0) {
|
||||||
|
LOGGING("NvLogRedirect: fmt string not found (vsnprintf@+%lx)", vsnprintf_addr - mapped_nso);
|
||||||
|
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t nvlog_addr = 0;
|
||||||
|
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + 2) <= textEnd; ++p) {
|
||||||
|
const uintptr_t pc = reinterpret_cast<uintptr_t>(p);
|
||||||
|
if (!AsmIsAdrp(p[0])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uintptr_t adrpPage = (pc & ~static_cast<uintptr_t>(0xFFFu)) + static_cast<uintptr_t>(AsmAdrpPageOffset(p[0]));
|
||||||
|
if (adrpPage != (strAddr & ~static_cast<uintptr_t>(0xFFFu))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const u32 reg = asm_get_rd(p[0]);
|
||||||
|
if (!(AsmIsAddImm64(p[1]) && asm_get_rd(p[1]) == reg && AsmGetRn(p[1]) == reg && AsmGetImm12(p[1]) == (strAddr & 0xFFFu))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (u32 k = 2; k <= 12 && (pc + (k + 1) * 4) <= textEnd; ++k) {
|
||||||
|
if (AsmIsBl(p[k])) { nvlog_addr = AsmBranchTarget(p[k], pc + k * 4); break; }
|
||||||
|
}
|
||||||
|
if (nvlog_addr != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nvlog_addr == 0 || nvlog_addr < mapped_nso || nvlog_addr >= textEnd) {
|
||||||
|
LOGGING("NvLogRedirect: NvLog entry not found (fmt@+%lx)", strAddr - mapped_nso);
|
||||||
|
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
||||||
|
}
|
||||||
|
|
||||||
|
const uintptr_t helper = CaveReserve(40);
|
||||||
|
if (helper == 0) {
|
||||||
|
LOGGING("NvLogRedirect: cave unavailable (cave=%lx size=%lx)",
|
||||||
|
static_cast<unsigned long>(g_pcv_cave), static_cast<unsigned long>(g_pcv_cave_size));
|
||||||
|
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
||||||
|
}
|
||||||
|
u32 *t = reinterpret_cast<u32 *>(helper);
|
||||||
|
size_t n = 0;
|
||||||
|
auto emit = [&](u32 ins) { t[n] = ins; ++n; };
|
||||||
|
|
||||||
|
emit(AsmMakeSubImm64(31, 31, 0x200));
|
||||||
|
emit(AsmMakeStpImm64(0, 1, 31, 0x100));
|
||||||
|
emit(AsmMakeStpImm64(2, 3, 31, 0x110));
|
||||||
|
emit(AsmMakeStpImm64(4, 5, 31, 0x120));
|
||||||
|
emit(AsmMakeStpImm64(6, 7, 31, 0x130));
|
||||||
|
emit(AsmMakeStpqImm(0, 1, 31, 0x140));
|
||||||
|
emit(AsmMakeStpqImm(2, 3, 31, 0x160));
|
||||||
|
emit(AsmMakeStpqImm(4, 5, 31, 0x180));
|
||||||
|
emit(AsmMakeStpqImm(6, 7, 31, 0x1A0));
|
||||||
|
emit(AsmMakeStrImm64(30, 31, 0x1E0));
|
||||||
|
emit(AsmMakeAddImm64(9, 31, 0x200)); emit(AsmMakeStrImm64(9, 31, 0x1C0)); /* __stack */
|
||||||
|
emit(AsmMakeAddImm64(9, 31, 0x140)); emit(AsmMakeStrImm64(9, 31, 0x1C8)); /* __gr_top */
|
||||||
|
emit(AsmMakeAddImm64(9, 31, 0x1C0)); emit(AsmMakeStrImm64(9, 31, 0x1D0)); /* __vr_top */
|
||||||
|
emit(AsmMakeMovnW(9, 0x37)); emit(AsmMakeStrImm32(9, 31, 0x1D8)); /* __gr_offs = -56 */
|
||||||
|
emit(AsmMakeMovnW(9, 0x7F)); emit(AsmMakeStrImm32(9, 31, 0x1DC)); /* __vr_offs = -128 */
|
||||||
|
emit(AsmMakeAddImm64(0, 31, 0x00)); /* mov x0,sp (buf) */
|
||||||
|
emit(AsmMakeMovzW(1, 0x100)); /* size = 0x100 */
|
||||||
|
emit(AsmMakeLdrImm64(2, 31, 0x100)); /* fmt (saved x0) */
|
||||||
|
emit(AsmMakeAddImm64(3, 31, 0x1C0)); /* ap */
|
||||||
|
emit(AsmMakeBl(helper + n * 4, vsnprintf_addr));
|
||||||
|
emit(AsmMakeMovReg(1, 0)); /* len = retval */
|
||||||
|
emit(AsmMakeCmpImm32(1, 0x100));
|
||||||
|
{ const size_t at = n; emit(AsmMakeBCond(helper + at * 4, helper + (at + 2) * 4, 0x3u)); } /* b.lo +2 */
|
||||||
|
emit(AsmMakeMovzW(1, 0xFF)); /* clamp len */
|
||||||
|
emit(AsmMakeAddImm64(0, 31, 0x00)); /* mov x0,sp (str) */
|
||||||
|
emit(AsmMakeSvc(0x27)); /* svcOutputDebugString */
|
||||||
|
emit(AsmMakeLdrImm64(30, 31, 0x1E0));
|
||||||
|
emit(AsmMakeAddImm64(31, 31, 0x200));
|
||||||
|
emit(RetIns);
|
||||||
|
|
||||||
|
/* Redirect the call sites as patching the actual function causes crash */
|
||||||
|
const uintptr_t roStart = g_pcv_cave + g_pcv_cave_size; /* module .rodata start */
|
||||||
|
size_t patchedSites = 0;
|
||||||
|
if (HOC_PCV_NVLOG_PATCH) {
|
||||||
|
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + 1) <= textEnd; ++p) {
|
||||||
|
if (!AsmIsBl(*p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uintptr_t pc = reinterpret_cast<uintptr_t>(p);
|
||||||
|
if (AsmBranchTarget(*p, pc) != nvlog_addr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool isFmtCall = false;
|
||||||
|
for (u32 j = 1; j <= 8 && reinterpret_cast<uintptr_t>(p - j) >= reinterpret_cast<uintptr_t>(nsoStart); ++j) {
|
||||||
|
const u32 w = *(p - j);
|
||||||
|
if (AsmIsAdrp(w) && asm_get_rd(w) == 0) { /* adrp x0,<page> */
|
||||||
|
const uintptr_t wpc = pc - j * 4;
|
||||||
|
const uintptr_t tgtPage = (wpc & ~static_cast<uintptr_t>(0xFFFu)) + static_cast<uintptr_t>(AsmAdrpPageOffset(w));
|
||||||
|
if (tgtPage >= (roStart & ~static_cast<uintptr_t>(0xFFFu))) { isFmtCall = true; break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isFmtCall) {
|
||||||
|
PATCH_OFFSET(p, AsmMakeBl(pc, helper));
|
||||||
|
++patchedSites;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGGING("NvLogRedirect: stub@+%lx vsnprintf@+%lx helper@+%lx instr=%zu sites=%zu",
|
||||||
|
nvlog_addr - mapped_nso, vsnprintf_addr - mapped_nso, helper - mapped_nso, n, patchedSites);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Relocate C2/C3Bus to avoid issues*/
|
||||||
|
Result BusFreqReloc(u32 *ptr) {
|
||||||
|
const u32 busReg = AsmGetRn(ptr[0]); /* ldr Xbuf,[Xbus,#0x10] : bus struct pointer */
|
||||||
|
const u32 bufReg = asm_get_rd(ptr[0]); /* : freq-buffer arg */
|
||||||
|
const u32 bufOff = AsmGetLdStImm64Off(ptr[0]); /* : bus->freqBuf offset */
|
||||||
|
const u32 cntReg = asm_get_rd(ptr[1]); /* add Xcnt,Xbus,#0x18 : arg2 (&count) */
|
||||||
|
const u32 railReg = asm_get_rd(ptr[2]); /* str Xrail,[Xbus,#0x50]: arg0 (rail) */
|
||||||
|
u32 *call = ptr + 3; /* the bl to relocate */
|
||||||
|
const uintptr_t realFn = AsmBranchTarget(*call, reinterpret_cast<uintptr_t>(call));
|
||||||
|
|
||||||
|
/* Pick 3 scratch registers */
|
||||||
|
u32 s[3], sc = 0;
|
||||||
|
for (u32 r = 9; r <= 15 && sc < 3; ++r) {
|
||||||
|
if (r != busReg && r != bufReg && r != cntReg && r != railReg) {
|
||||||
|
s[sc++] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R_UNLESS(sc == 3, ldr::ResultInvalidBusFreqReloc());
|
||||||
|
|
||||||
|
const uintptr_t tramp = CaveReserve(9);
|
||||||
|
R_UNLESS(tramp != 0, ldr::ResultInvalidBusFreqReloc());
|
||||||
|
|
||||||
|
const uintptr_t region = g_pcv_scratch + HocBusFreqBufOffset; /* [0]=counter, +0x10 + i*0x400 = bufs */
|
||||||
|
u32 *t = reinterpret_cast<u32 *>(tramp);
|
||||||
|
size_t n = 0;
|
||||||
|
auto emit = [&](u32 ins) { t[n] = ins; ++n; };
|
||||||
|
emit(AsmMakeAdrp(tramp + n * 4, region, s[0])); /* adrp s0,<region> */
|
||||||
|
emit(AsmMakeAddImm64(s[0], s[0], region & 0xFFFu)); /* add s0,s0,#lo */
|
||||||
|
emit(AsmMakeLdrImm32(s[1], s[0], 0x00)); /* s1 = counter */
|
||||||
|
emit(AsmMakeAddImm64(s[2], s[1], 1)); /* s2 = counter+1 */
|
||||||
|
emit(AsmMakeStrImm32(s[2], s[0], 0x00)); /* counter++ */
|
||||||
|
emit(AsmMakeAddImm64(s[0], s[0], 0x10)); /* s0 = region+0x10 (buffers) */
|
||||||
|
emit(AsmMakeAddShiftedReg64(bufReg, s[0], s[1], 10)); /* Xbuf = s0 + counter*0x400 */
|
||||||
|
emit(AsmMakeStrImm64(bufReg, busReg, bufOff)); /* bus[freqBuf] = Xbuf */
|
||||||
|
emit(AsmMakeB(tramp + n * 4, realFn)); /* tail-call the real function */
|
||||||
|
|
||||||
|
PATCH_OFFSET(call, AsmMakeBl(reinterpret_cast<uintptr_t>(call), tramp));
|
||||||
|
const uintptr_t base = reinterpret_cast<uintptr_t>(nsoStart);
|
||||||
|
(void) base;
|
||||||
|
LOGGING("BusFreqReloc: call@+%lx -> tramp@+%lx realfn@+%lx (bus=x%u buf=x%u off=0x%x scratch=x%u,x%u,x%u)",
|
||||||
|
reinterpret_cast<uintptr_t>(call) - base, tramp - base, realFn - base, busReg, bufReg, bufOff, s[0], s[1], s[2]);
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if HOC_UART_LOG
|
||||||
|
/* Force GetEffectiveVerbosityLevel to return a non-zero level so all NvLog runs. */
|
||||||
|
Result ForceVerbosity(u32 *ptr) {
|
||||||
|
PATCH_OFFSET(&ptr[0], AsmMakeMovzW(0, static_cast<u16>(HOC_PCV_FORCE_VERBOSITY))); /* movz w0,#level */
|
||||||
|
PATCH_OFFSET(&ptr[1], RetIns); /* ret */
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Widen InitDram for a >32-entry EMC DVFS list. Freq array can be dropped to free 264 bytes, relocate the Soc LUT to that space */
|
||||||
|
Result EmcSocLutReloc(u32 *ptr) {
|
||||||
|
constexpr u32 Window = 48;
|
||||||
|
|
||||||
|
u32 *freqStore = ScanAssembly(ptr - Window, Window, EmcSocFreqStoreAsm, asm_compare_no_rd); /* str x?,[x8,#0x18] */
|
||||||
|
u32 *voltStore = ScanAssembly(ptr - Window, Window, EmcSocVoltStoreAsm, asm_compare_no_rd); /* str w?,[x8,#0x48] */
|
||||||
|
u32 *readLoad = ScanAssembly(ptr - Window, Window, EmcSocReadLoadAsm, asm_compare_no_rd); /* ldr w?,[x9,#0x48] */
|
||||||
|
R_UNLESS(freqStore && voltStore && readLoad, ldr::ResultInvalidEmcSocLut());
|
||||||
|
|
||||||
|
u32 *voltBase = voltStore - 2; /* `add Xb,Xsrc,Xi,LSL#2` (a cmn sits between it and store) */
|
||||||
|
R_UNLESS(AsmIsAddShiftedReg64(*voltBase) && asm_get_rd(*voltBase) == AsmGetRn(*voltStore),
|
||||||
|
ldr::ResultInvalidEmcSocLut());
|
||||||
|
|
||||||
|
/* adrp Xl ; add Xl,Xl,#off ; ... ; str Xl,[rail,#0x120] */
|
||||||
|
const u32 lutReg = asm_get_rd(ptr[0]);
|
||||||
|
const u32 railReg = AsmGetRn(ptr[0]);
|
||||||
|
R_UNLESS(AsmIsAdrp(ptr[-3]) && asm_get_rd(ptr[-3]) == lutReg, ldr::ResultInvalidEmcSocLut());
|
||||||
|
R_UNLESS(AsmIsAddImm64(ptr[-2]) && asm_get_rd(ptr[-2]) == lutReg && AsmGetRn(ptr[-2]) == lutReg,
|
||||||
|
ldr::ResultInvalidEmcSocLut());
|
||||||
|
|
||||||
|
const u32 srcBase = AsmGetRn(*voltBase); /* rail ptr at +0x20 */
|
||||||
|
const u32 wBase = asm_get_rd(*voltBase); /* base reg */
|
||||||
|
const u32 wIdx = AsmGetRm(*voltBase); /* loop index */
|
||||||
|
|
||||||
|
PATCH_OFFSET(freqStore, NopIns); /* Unneeded */
|
||||||
|
PATCH_OFFSET(voltBase, AsmMakeLdrImm64(wBase, srcBase, 0x20)); /* ldr Xb,[Xsrc,#0x20] (rail) */
|
||||||
|
PATCH_OFFSET(voltStore - 1, AsmMakeAddImm64(wBase, wBase, 0x18)); /* add Xb,Xb,#0x18 (was cmn) */
|
||||||
|
PATCH_OFFSET(voltStore, AsmSetLdStRegOffset(*voltStore, wIdx)); /* str Wv,[Xb,Xi,LSL#2] -> rail+0x18+i*4 */
|
||||||
|
PATCH_OFFSET(voltStore + 1, NopIns); /* Unneeded */
|
||||||
|
|
||||||
|
/* rail+0x18 as the socMinLut pointer. */
|
||||||
|
PATCH_OFFSET(ptr - 3, NopIns);
|
||||||
|
PATCH_OFFSET(ptr - 2, AsmMakeAddImm64(lutReg, railReg, 0x18));/* add Xl,rail,#0x18 */
|
||||||
|
|
||||||
|
/* Drop the abort branch in case of a bad read */
|
||||||
|
for (u32 i = 1; i <= 4; ++i) {
|
||||||
|
if (AsmIsBCond(readLoad[i])) {
|
||||||
|
PATCH_OFFSET(&readLoad[i], NopIns);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result EmcDvfsCountLimit(u32 *ptr) {
|
||||||
|
R_UNLESS(EmcDvfsCountPatternFn(ptr), ldr::ResultInvalidEmcDvfsCount());
|
||||||
|
|
||||||
|
/* cmp w?,#0x21 -> cmp w?,#EmcDvfsTableEntryCount */
|
||||||
|
PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast<u16>(EmcDvfsTableEntryCount)));
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result EmcRateListLimit(u32 *ptr) {
|
||||||
|
/* ptr = cmp w?,#0x20 ; ptr[1] = csel w?,w?,w?,lt (w? = min(maxCount, 32)) ; ptr[2] = bl */
|
||||||
|
R_UNLESS(EmcRateListPatternFn(ptr), ldr::ResultInvalidEmcRateList());
|
||||||
|
|
||||||
|
/* The csel's Rm holds the 32 cap. */
|
||||||
|
const u32 capReg = AsmGetRm(ptr[1]);
|
||||||
|
const u32 capMov = AsmMakeMovzW(capReg, 0x20); /* movz w<Rm>,#0x20 */
|
||||||
|
|
||||||
|
u32 *movPtr = nullptr;
|
||||||
|
for (u32 i = 1; i <= 16; ++i) {
|
||||||
|
if (*(ptr - i) == capMov) {
|
||||||
|
movPtr = ptr - i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R_UNLESS(movPtr, ldr::ResultInvalidEmcRateList());
|
||||||
|
|
||||||
|
/* min(maxCount, 32) -> min(maxCount, EmcDvfsTableEntryCount). */
|
||||||
|
PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast<u16>(EmcDvfsTableEntryCount))); /* cmp w?,#64 */
|
||||||
|
PATCH_OFFSET(movPtr, asm_set_imm16(*movPtr, static_cast<u16>(EmcDvfsTableEntryCount))); /* movz w?,#64 */
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) {
|
||||||
|
struct {
|
||||||
|
u8 reg;
|
||||||
|
u8 val;
|
||||||
|
} __attribute__((packed)) cmd;
|
||||||
|
|
||||||
|
I2cSession _session;
|
||||||
|
R_TRY(i2cOpenSession(&_session, dev));
|
||||||
|
|
||||||
|
cmd.reg = reg;
|
||||||
|
cmd.val = val;
|
||||||
|
Result res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
|
||||||
|
i2csessionClose(&_session);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result EmcVddqVolt(u32 *ptr) {
|
||||||
|
regulator *entry = reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_2_3.default_uv));
|
||||||
|
|
||||||
|
constexpr u32 uv_step = 5'000;
|
||||||
|
constexpr u32 uv_min = 250'000;
|
||||||
|
|
||||||
|
auto validator = [entry]() {
|
||||||
|
R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
|
||||||
|
R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
|
||||||
|
R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
|
||||||
|
R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
|
||||||
|
R_SUCCEED();
|
||||||
|
};
|
||||||
|
|
||||||
|
R_TRY(validator());
|
||||||
|
|
||||||
|
u32 emc_uv = C.marikoEmcVddqVolt;
|
||||||
|
|
||||||
|
if (!emc_uv) {
|
||||||
|
R_SKIP();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emc_uv % uv_step) {
|
||||||
|
emc_uv = (emc_uv + uv_step - 1) / uv_step * uv_step; // rounding
|
||||||
|
}
|
||||||
|
|
||||||
|
PATCH_OFFSET(ptr, emc_uv);
|
||||||
|
|
||||||
|
i2cInitialize();
|
||||||
|
Result resultI2C = I2cSet_U8(I2cDevice_Max77812_2, 0x25, (emc_uv - uv_min) / uv_step);
|
||||||
|
i2cExit();
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
|
||||||
|
return resultI2C;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetSocSpeedo(u32 &socSpeedo) {
|
||||||
|
constexpr u64 FusePhysicalAddress = 0x7000F000;
|
||||||
|
u64 virtualAddress = 0;
|
||||||
|
constexpr u64 Size = 0x1000;
|
||||||
|
|
||||||
|
u64 outSize;
|
||||||
|
/* TODO: use svc::QueryMemoryMapping instead. */
|
||||||
|
R_TRY(svcQueryMemoryMapping(&virtualAddress, &outSize, FusePhysicalAddress, Size));
|
||||||
|
|
||||||
|
constexpr u32 FuseOffset = 2048;
|
||||||
|
constexpr u32 SocSpeedoOffset = 308;
|
||||||
|
socSpeedo = *reinterpret_cast<u32 *>(virtualAddress + FuseOffset + SocSpeedoOffset);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetSocProcessId(u32 socSpeedo) {
|
||||||
|
if (socSpeedo <= 1597) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socSpeedo <= 1708) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* >= 1709. */
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocVoltAsm(u32 *compareSpeedos) {
|
||||||
|
constexpr u32 VoltageScanLimit = 10;
|
||||||
|
/* Might actually be speedo id. */
|
||||||
|
u32 *writeProcessId = ScanAssembly(compareSpeedos, VoltageScanLimit, SocVoltWriteProcessIdAsm, asm_compare_no_rd);
|
||||||
|
R_UNLESS(writeProcessId != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||||
|
u8 writeProcessIdRd = asm_get_rd(*writeProcessId);
|
||||||
|
|
||||||
|
/* This writes 1050mV. */
|
||||||
|
u32 *writeVoltage = ScanAssembly(writeProcessId, VoltageScanLimit, SocVoltWriteVoltageAsm, asm_compare_no_rd);
|
||||||
|
R_UNLESS(writeVoltage != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||||
|
u8 writeVoltageRd = asm_get_rd(*writeVoltage);
|
||||||
|
|
||||||
|
/* A csel instruction is used to select the soc voltage limit register. */
|
||||||
|
/* We care about its destination register since that is used for verification. */
|
||||||
|
constexpr u32 VoltageSelectScanLimit = 24;
|
||||||
|
u32 *selectVoltage = ScanAssembly(writeVoltage, VoltageSelectScanLimit, SocVoltSelectRegisterAsm, AsmCompareCselNoReg);
|
||||||
|
R_UNLESS(selectVoltage != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||||
|
/* Todo: check rm and rn? */
|
||||||
|
u8 selectVoltageRd = asm_get_rd(*selectVoltage);
|
||||||
|
|
||||||
|
/* rdCsel is then multiplied by 1000 to convert to uV. */
|
||||||
|
/* This is pretty far down the function. */
|
||||||
|
constexpr u32 MultiplierScanLimit = 200;
|
||||||
|
u32 *multiplier = ScanAssembly(selectVoltage, MultiplierScanLimit, SocVoltMultiplyVoltsAsm, AsmCompareMullNoReg);
|
||||||
|
R_UNLESS(multiplier != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||||
|
u8 multiplierRn = AsmGetMullRn(*multiplier);
|
||||||
|
u8 multiplierRm = AsmGetMullRm(*multiplier);
|
||||||
|
/* One of the two registers has to be rdCsel. */
|
||||||
|
R_UNLESS((multiplierRn == selectVoltageRd) || (multiplierRm == selectVoltageRd), ldr::ResultInvalidSocVoltPattern());
|
||||||
|
u8 multiplierRd = asm_get_rd(*multiplier);
|
||||||
|
|
||||||
|
/* Subs instruction is then used to verify against absolute limit. */
|
||||||
|
u32 limitValidationPattern = AsmSubsSetRn(SocVoltValidateLimitAsm, multiplierRd);
|
||||||
|
u32 *limitValidation = ScanAssembly(multiplier, VoltageScanLimit, limitValidationPattern, AsmSubsCompareNoReg);
|
||||||
|
R_UNLESS(limitValidation != nullptr, ldr::ResultInvalidSocVoltPattern());
|
||||||
|
|
||||||
|
/* There is a b.gt instruction right after (checks for socVoltageCap < socVoltageMax). */
|
||||||
|
u32 *branchToAbort = limitValidation + 1;
|
||||||
|
R_UNLESS(AsmCompareBrConNoImm19(*branchToAbort, SocVoltBranchToAbortAsm), ldr::ResultInvalidSocVoltPattern());
|
||||||
|
|
||||||
|
if (!C.marikoSocVmax || C.marikoSocVmax <= 1000) {
|
||||||
|
R_SKIP();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adjust 1598 speedo minimum to ensure it always goes down process id 0 branch. */
|
||||||
|
/* 2200 should be high enough :D */
|
||||||
|
u32 compareSpeedosPatch = AsmSubsSetImm12(*compareSpeedos, 2200);
|
||||||
|
PATCH_OFFSET(compareSpeedos, compareSpeedosPatch);
|
||||||
|
|
||||||
|
u32 socSpeedo = 0;
|
||||||
|
R_TRY(GetSocSpeedo(socSpeedo));
|
||||||
|
|
||||||
|
/* Adjust processId from 0 to [process id of switch booting this]. */
|
||||||
|
/* We're overwriting the orr instruction entirly. */
|
||||||
|
u32 processId = GetSocProcessId(socSpeedo);
|
||||||
|
u32 writeProcessIdPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, processId), writeProcessIdRd);
|
||||||
|
PATCH_OFFSET(writeProcessId, writeProcessIdPatch);
|
||||||
|
|
||||||
|
/* Adjust voltage limit. */
|
||||||
|
u32 voltageLimitPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, C.marikoSocVmax), writeVoltageRd);
|
||||||
|
PATCH_OFFSET(writeVoltage, voltageLimitPatch);
|
||||||
|
|
||||||
|
/* Branches to an abort if limits are invalid -- we patch the branch instruction with NOP. */
|
||||||
|
PATCH_OFFSET(branchToAbort, NopIns);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SocVoltLimit(u32 *ptr) {
|
||||||
|
R_UNLESS(!std::memcmp(ptr - SocVoltLimitMaxDefaultIndex, socVoltLimitArray, sizeof(socVoltLimitArray)), ldr::ResultInvalidSocVoltLimit());
|
||||||
|
if (!C.marikoSocVmax || C.marikoSocVmax <= SocVoltLimitOfficial) {
|
||||||
|
R_SKIP();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 Step = 25;
|
||||||
|
u32 maxVolt = C.marikoSocVmax;
|
||||||
|
if (maxVolt % Step) {
|
||||||
|
maxVolt = maxVolt / Step * Step; /* Round. */
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 volt = SocVoltLimitOfficial;
|
||||||
|
for (u32 i = 1; i < DvfsTableEntryCount - SocVoltLimitMaxDefaultIndex && volt < maxVolt; ++i) {
|
||||||
|
volt += Step;
|
||||||
|
PATCH_OFFSET(ptr + i, volt);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result EmcRateSessLimit(u32 *ptr) {
|
||||||
|
u32 movzI = 0;
|
||||||
|
R_UNLESS(EmcRateSessFindClamp(ptr, nullptr, nullptr, &movzI), ldr::ResultInvalidEmcRateList());
|
||||||
|
|
||||||
|
/* Reject cmd11 GetDvfsTable. */
|
||||||
|
for (u32 i = 1; i <= 24; ++i) {
|
||||||
|
const u32 w = ptr[i];
|
||||||
|
if (AsmIsSubX29Imm(w) && AsmGetImm12(w) >= 0x20u) { /* sub x?,x29,#>=0x20 */
|
||||||
|
R_THROW(ldr::ResultInvalidEmcRateList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mov x<desc>,x2 */
|
||||||
|
u32 descReg = 0xFFu;
|
||||||
|
for (u32 i = 1; i <= 24; ++i) {
|
||||||
|
if (AsmIsMovReg(ptr[i], 2)) { descReg = asm_get_rd(ptr[i]); break; }
|
||||||
|
}
|
||||||
|
R_UNLESS(descReg != 0xFFu, ldr::ResultInvalidEmcRateList());
|
||||||
|
|
||||||
|
/* Repoint the duplicated-imm pair */
|
||||||
|
u32 *adds[8]; u32 addImm[8]; u32 nAdds = 0;
|
||||||
|
for (u32 i = 1; i <= 24 && nAdds < 8; ++i) {
|
||||||
|
const u32 w = ptr[i];
|
||||||
|
if (AsmIsAddSpImm(w)) { /* add x?,sp,#imm12 (shift 0) */
|
||||||
|
adds[nAdds] = ptr + i;
|
||||||
|
addImm[nAdds] = AsmGetImm12(w);
|
||||||
|
++nAdds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
u32 patched = 0;
|
||||||
|
for (u32 a = 0; a < nAdds; ++a) {
|
||||||
|
bool dup = false;
|
||||||
|
for (u32 b = 0; b < nAdds; ++b) {
|
||||||
|
if (a != b && addImm[a] == addImm[b]) { dup = true; break; }
|
||||||
|
}
|
||||||
|
if (dup) {
|
||||||
|
PATCH_OFFSET(adds[a], AsmMakeLdrImm64(asm_get_rd(*adds[a]), descReg, 0)); /* ldr x?,[x<desc>] */
|
||||||
|
++patched;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R_UNLESS(patched == 2, ldr::ResultInvalidEmcRateList());
|
||||||
|
|
||||||
|
/* min(maxCount, 32) -> min(maxCount, EmcDvfsTableEntryCount) */
|
||||||
|
PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast<u16>(EmcDvfsTableEntryCount))); /* cmp w?,#64 */
|
||||||
|
PATCH_OFFSET(ptr + movzI, asm_set_imm16(*(ptr + movzI), static_cast<u16>(EmcDvfsTableEntryCount))); /* movz w?,#64 */
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||||
|
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
||||||
|
|
||||||
|
g_pcv_scratch = mapped_nso + nso_size - HocPcvScratchSize;
|
||||||
|
g_nso_size = nso_size;
|
||||||
|
g_cave_cursor = g_pcv_cave; /* start the .text-cave bump allocator (0 if unavailable) */
|
||||||
|
|
||||||
|
MtcGenerateFreqTables();
|
||||||
|
|
||||||
|
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
||||||
|
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
|
||||||
|
|
||||||
|
PatcherEntry<u32> patches[] = {
|
||||||
|
{ "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit },
|
||||||
|
{ "CPU Freq Table", CpuFreqCvbTable<true>, 1, nullptr, CpuCvbDefaultMaxFreq },
|
||||||
|
{ "CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial },
|
||||||
|
{ "CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial },
|
||||||
|
{ "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low },
|
||||||
|
{ "GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial },
|
||||||
|
{ "GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
|
||||||
|
{ "GPU Freq Table", GpuFreqCvbTable<true>, 1, nullptr, GpuCvbDefaultMaxFreq },
|
||||||
|
{ "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
|
||||||
|
{ "GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
|
||||||
|
{ "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
|
||||||
|
{ "MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
|
||||||
|
{ "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit },
|
||||||
|
{ "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
|
||||||
|
{ "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
|
||||||
|
{ "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault },
|
||||||
|
{ "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default },
|
||||||
|
{ "MEM Table Asm", &MemMtcTableAsm, 1, &MemMtcGetGetTablePatternFn },
|
||||||
|
{ "EMC DVFS Count", &EmcDvfsCountLimit, 1, &EmcDvfsCountPatternFn },
|
||||||
|
{ "EMC SoC LUT", &EmcSocLutReloc, 1, &EmcSocLutPatternFn },
|
||||||
|
{ "EMC Rate List", &EmcRateListLimit, 0, &EmcRateListPatternFn },
|
||||||
|
{ "EMC Rate Sess", &EmcRateSessLimit, 1, &EmcRateSessPatternFn },
|
||||||
|
{ "Bus Freq Reloc", &BusFreqReloc, 1, &BusFreqRelocPatternFn },
|
||||||
|
{ "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn },
|
||||||
|
{ "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial },
|
||||||
|
/* Debugging patches */
|
||||||
|
#if HOC_UART_LOG
|
||||||
|
{ "NvLog Redirect", &NvLogUartRedirect, 1, &NvLogVsnprintfPatternFn, 0, 0, true },
|
||||||
|
{ "Force Verbosity", &ForceVerbosity, 3, &ForceVerbosityPatternFn, 0, 0, true },
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) {
|
||||||
|
u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
|
||||||
|
for (auto &entry : patches) {
|
||||||
|
if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &entry : patches) {
|
||||||
|
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
||||||
|
if (R_FAILED(entry.CheckResult())) {
|
||||||
|
panic::SmcError(panic::Patch);
|
||||||
|
|
||||||
|
CRASH(entry.description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+131
-4
@@ -20,12 +20,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../oc_common.hpp"
|
#include "../../oc_common.hpp"
|
||||||
#include "pcv_common.hpp"
|
#include "../pcv_common.hpp"
|
||||||
#include "pcv_asm.hpp"
|
#include "../pcv_asm.hpp"
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::mariko {
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
|
|
||||||
|
extern u32 *nsoStart;
|
||||||
|
|
||||||
constexpr cvb_entry_t CpuCvbTableDefault[] = {
|
constexpr cvb_entry_t CpuCvbTableDefault[] = {
|
||||||
{ 204000, { 721589, -12695, 27 }, { } },
|
{ 204000, { 721589, -12695, 27 }, { } },
|
||||||
{ 306000, { 747134, -14195, 27 }, { } },
|
{ 306000, { 747134, -14195, 27 }, { } },
|
||||||
@@ -60,7 +62,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 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, 2'805'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, };
|
||||||
|
|
||||||
constexpr cvb_entry_t GpuCvbTableDefault[] = {
|
constexpr cvb_entry_t GpuCvbTableDefault[] = {
|
||||||
// GPUB01_NA_CVB_TABLE
|
// GPUB01_NA_CVB_TABLE
|
||||||
@@ -299,6 +301,131 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
|
return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr u32 EmcCountCmpAsm = 0x7100851F; /* cmp w?,#0x21 (subs wzr,w?,#0x21) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
str <lut>,[<rail>,#0x120] ; volt-array pointer
|
||||||
|
str w?, [<rail>,#0x154] ; num_freqs
|
||||||
|
*/
|
||||||
|
constexpr u32 EmcSocLutPtrStoreAsm = 0xF9009009; /* str x?,[x0,#0x120] (anchor) */
|
||||||
|
constexpr u32 EmcSocLutCountStoreAsm = 0xB9015408; /* str w?,[x0,#0x154] (anchor) */
|
||||||
|
constexpr u32 EmcSocFreqStoreAsm = 0xF9000D00; /* str x?,[x8,#0x18] */
|
||||||
|
constexpr u32 EmcSocVoltStoreAsm = 0xB9004900; /* str w?,[x8,#0x48] (socMinLut[i]) */
|
||||||
|
constexpr u32 EmcSocReadLoadAsm = 0xB9404929; /* ldr w?,[x9,#0x48] (socMinLut readback) */
|
||||||
|
|
||||||
|
inline bool EmcDvfsCountPatternFn(u32 *ptr) {
|
||||||
|
/* Local context: cbz w?,<skip> ; cmp w?,#0x21 ; b.cs <abort> */
|
||||||
|
return asm_compare_no_rd(*ptr, EmcCountCmpAsm) && AsmCompareBrConNoImm19(*(ptr + 1), 0x54000002) /* b.cs */
|
||||||
|
&& AsmCbzCompareOpcodeOnly(*(ptr - 1), 0x34000000); /* cbz */
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool EmcSocLutPatternFn(u32 *ptr) {
|
||||||
|
return asm_compare_no_rd(*ptr, EmcSocLutPtrStoreAsm) /* str x?,[x0,#0x120] */
|
||||||
|
&& asm_compare_no_rd(*(ptr + 1), EmcSocLutCountStoreAsm); /* str w?,[x0,#0x154] */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
mov w?,#0x20 ; the 32 cap
|
||||||
|
cmp w?,#0x20
|
||||||
|
csel w?,w?,w?,lt ; w? = min(maxCount, 32)
|
||||||
|
bl TegraGetEmcDvfsFreqTable
|
||||||
|
|
||||||
|
cmp w?,#0x20 ; (maxCount)
|
||||||
|
csel w?,<same>,<cap>,lt ; min(maxCount, 32)
|
||||||
|
bl <Get*DvfsFreqTable>
|
||||||
|
*/
|
||||||
|
constexpr u32 EmcRateCapCmpAsm = 0x710082FF; /* cmp w?,#0x20 */
|
||||||
|
constexpr u32 EmcRateCapCselAsm = 0x1A80B000; /* csel w?,w?,w?,lt */
|
||||||
|
|
||||||
|
inline bool EmcRateListPatternFn(u32 *ptr) {
|
||||||
|
return AsmSubsCompareNoReg(*ptr, EmcRateCapCmpAsm) /* cmp w?,#0x20 */
|
||||||
|
&& AsmCompareCselNoReg(*(ptr + 1), EmcRateCapCselAsm) /* csel w?,w?,w?,lt */
|
||||||
|
&& (AsmGetRn(*ptr) == AsmGetRn(*(ptr + 1))) /* min(reg, 0x20) */
|
||||||
|
&& AsmBlCompareOpcodeOnly(*(ptr + 2), 0x94000000); /* bl <Get*DvfsFreqTable> */
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr u32 EmcRateSessCmpAsm = 0x710082FF; /* cmp w?,#0x20 */
|
||||||
|
constexpr u32 EmcRateSessMovAsm = 0x52800400; /* movz w?,#0x20 */
|
||||||
|
constexpr u32 EmcRateSessCselAsm = 0x1A80B000; /* csel w?,w?,w?,lt (opcode + cond) */
|
||||||
|
|
||||||
|
inline bool EmcRateSessFindClamp(u32 *ptr, u32 *out_c, u32 *out_cap, u32 *out_movz_i) {
|
||||||
|
if (!AsmSubsCompareNoReg(ptr[0], EmcRateSessCmpAsm)) return false; /* cmp w<c>,#0x20 */
|
||||||
|
const u32 c = AsmGetRn(ptr[0]);
|
||||||
|
for (u32 i = 1; i <= 14; ++i) {
|
||||||
|
const u32 w = ptr[i];
|
||||||
|
if (AsmCompareCselNoReg(w, EmcRateSessCselAsm) && AsmGetRn(w) == c && asm_get_rd(w) == c) {
|
||||||
|
const u32 cap = AsmGetRm(w);
|
||||||
|
for (u32 j = 1; j < i; ++j) {
|
||||||
|
if ((ptr[j] & 0xFFFFFFE0u) == EmcRateSessMovAsm && asm_get_rd(ptr[j]) == cap) {
|
||||||
|
if (out_c) *out_c = c;
|
||||||
|
if (out_cap) *out_cap = cap;
|
||||||
|
if (out_movz_i) *out_movz_i = j;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool EmcRateSessPatternFn(u32 *ptr) {
|
||||||
|
return EmcRateSessFindClamp(ptr, nullptr, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool BusFreqRelocPatternFn(u32 *ptr) {
|
||||||
|
if (g_pcv_scratch == 0 || g_pcv_cave == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (reinterpret_cast<uintptr_t>(ptr + 4) > g_pcv_cave) { /* the call site lives in .text */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(AsmIsLdrImm64(ptr[0]) && AsmGetLdStImm64Off(ptr[0]) == 0x10)) return false; /* ldr Xbuf,[Xbus,#0x10] */
|
||||||
|
if (!(AsmIsAddImm64(ptr[1]) && AsmGetImm12(ptr[1]) == 0x18)) return false; /* add Xcnt,Xbus,#0x18 */
|
||||||
|
if (!(AsmIsStrImm64(ptr[2]) && AsmGetLdStImm64Off(ptr[2]) == 0x50)) return false; /* str Xrail,[Xbus,#0x50]*/
|
||||||
|
if (!AsmIsBl(ptr[3])) return false; /* bl GetDvfsRailUnique */
|
||||||
|
const u32 bus = AsmGetRn(ptr[0]);
|
||||||
|
return AsmGetRn(ptr[1]) == bus && AsmGetRn(ptr[2]) == bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool ForceVerbosityPatternFn(u32 *ptr) {
|
||||||
|
if (HOC_PCV_FORCE_VERBOSITY == 0 || g_pcv_cave == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (reinterpret_cast<uintptr_t>(ptr + 11) > g_pcv_cave) { /* .text only */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ptr[0] != 0xA9BE7BFDu || ptr[1] != 0xF9000BF3u || ptr[2] != 0x910003FDu) return false; /* stp/str/mov x29,sp */
|
||||||
|
if (!(AsmIsAddImm64(ptr[3]) && asm_get_rd(ptr[3]) == 0 && AsmGetRn(ptr[3]) == 29)) return false; /* add x0,x29,#imm */
|
||||||
|
if (!(AsmIsAddImm64(ptr[4]) && asm_get_rd(ptr[4]) == 19 && AsmGetRn(ptr[4]) == 29)) return false; /* add x19,x29,#imm */
|
||||||
|
if (AsmGetImm12(ptr[3]) != AsmGetImm12(ptr[4]) || !AsmIsBl(ptr[5])) return false;
|
||||||
|
for (u32 j = 6; j <= 10; ++j) {
|
||||||
|
if (ptr[j] == 0x7100001Fu) { /* cmp w0,#0 */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vsnprintf(buf,size,fmt,va_list) prologue */
|
||||||
|
inline constexpr u32 NvLogVsnSig[] = { 0xD10483FFu, 0xA9107BFDu, 0xF9008BFCu, 0x910403FDu, 0xF100003Fu };
|
||||||
|
|
||||||
|
inline bool NvLogVsnprintfPatternFn(u32 *ptr) {
|
||||||
|
if (HOC_UART_LOG == 0 || g_pcv_cave == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (reinterpret_cast<uintptr_t>(ptr + std::size(NvLogVsnSig)) > g_pcv_cave) { /* must sit in .text */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t k = 0; k < std::size(NvLogVsnSig); ++k) {
|
||||||
|
if (ptr[k] != NvLogVsnSig[k]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pcv.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
|
|
||||||
|
u32 CapCpuClock() {
|
||||||
|
u32 cpuCap = allowedCpuMaxFrequencies[0];
|
||||||
|
|
||||||
|
for (u32 freq : allowedCpuMaxFrequencies) {
|
||||||
|
if (C.marikoCpuMaxClock >= freq) {
|
||||||
|
cpuCap = freq;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cpuCap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CpuFreqVdd(u32 *ptr) {
|
||||||
|
dvfs_rail *entry = reinterpret_cast<dvfs_rail *>(reinterpret_cast<u8 *>(ptr) - offsetof(dvfs_rail, freq));
|
||||||
|
|
||||||
|
R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry());
|
||||||
|
R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||||
|
R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||||
|
R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry());
|
||||||
|
|
||||||
|
if (C.marikoCpuUVHigh) {
|
||||||
|
PATCH_OFFSET(ptr, CapCpuClock());
|
||||||
|
} else {
|
||||||
|
PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CpuVoltDVFS(u32 *ptr) {
|
||||||
|
CvbMeta *cpuCvbMeta = reinterpret_cast<CvbMeta *>(reinterpret_cast<u8 *>(ptr) - offsetof(CvbMeta, vmin));
|
||||||
|
|
||||||
|
R_UNLESS(cpuCvbMeta->highVmin == CpuHighVminOfficial, ldr::ResultInvalidCpuMinVolt());
|
||||||
|
R_UNLESS(cpuCvbMeta->unkStepMaybe == 38, ldr::ResultInvalidCpuMinVolt());
|
||||||
|
R_UNLESS(cpuCvbMeta->vmax == CpuVoltOfficial, ldr::ResultInvalidCpuMinVolt());
|
||||||
|
R_UNLESS(cpuCvbMeta->unkScale2 == 1000, ldr::ResultInvalidCpuMinVolt());
|
||||||
|
R_UNLESS(cpuCvbMeta->speedoScale == 100, ldr::ResultInvalidCpuMinVolt());
|
||||||
|
R_UNLESS(cpuCvbMeta->voltageScale == 1000, ldr::ResultInvalidCpuMinVolt());
|
||||||
|
R_UNLESS(cpuCvbMeta->unkZero5 == 0, ldr::ResultInvalidCpuMinVolt());
|
||||||
|
|
||||||
|
if (C.marikoCpuLowVmin) {
|
||||||
|
PATCH_OFFSET(&(cpuCvbMeta->vmin), C.marikoCpuLowVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.marikoCpuHighVmin) {
|
||||||
|
PATCH_OFFSET(&(cpuCvbMeta->highVmin), C.marikoCpuHighVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.marikoCpuMaxVolt) {
|
||||||
|
PATCH_OFFSET(&(cpuCvbMeta->vmax), C.marikoCpuMaxVolt);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CpuVoltThermals(u32 *ptr) {
|
||||||
|
if (std::memcmp(ptr, cpuVoltThermalData, sizeof(cpuVoltThermalData))) {
|
||||||
|
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.marikoCpuLowVmin) {
|
||||||
|
PATCH_OFFSET(ptr, C.marikoCpuLowVmin);
|
||||||
|
PATCH_OFFSET(ptr + 3, C.marikoCpuLowVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.marikoCpuMaxVolt) {
|
||||||
|
PATCH_OFFSET(ptr - 2, C.marikoCpuMaxVolt);
|
||||||
|
PATCH_OFFSET(ptr - 5, C.marikoCpuMaxVolt);
|
||||||
|
PATCH_OFFSET(ptr + 1, C.marikoCpuMaxVolt);
|
||||||
|
PATCH_OFFSET(ptr + 4, C.marikoCpuMaxVolt);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result CpuVoltDfll(u32 *ptr) {
|
||||||
|
CvbCpuDfllData *entry = reinterpret_cast<CvbCpuDfllData *>(ptr);
|
||||||
|
|
||||||
|
R_UNLESS(entry->tune0_low == 0xFFCF, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
R_UNLESS(entry->tune1_low == 0x12207FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
R_UNLESS(entry->tune1_high == 0x3FFF7FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
||||||
|
|
||||||
|
switch (C.marikoCpuUVLow) {
|
||||||
|
case 1:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_low), 0xffa0);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21107ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x0);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21107ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27207ff);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_low), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21107ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27307ff);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_low), 0xffff);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21107ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27407ff);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21607ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27707ff);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21607ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27807ff);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21607ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff);
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff);
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27c07ff);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27d07ff);
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27e07ff);
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27f07ff);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (C.marikoCpuUVHigh) {
|
||||||
|
case 1:
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x0);
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffff);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27207ff);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27307ff);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27407ff);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27707ff);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27807ff);
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
case 8:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff);
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27c07ff);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27d07ff);
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27e07ff);
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
||||||
|
PATCH_OFFSET(&(entry->tune1_high), 0x27f07ff);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+17
-8
@@ -1,5 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) Atmosphère-NX
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer and Horizon OC Contributors
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms and conditions of the GNU General Public License,
|
* under the terms and conditions of the GNU General Public License,
|
||||||
@@ -14,12 +20,15 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define __ACCESS_TABLE_NAME__ Emc01AccessTable
|
#pragma once
|
||||||
#define __ACCESS_TABLE_ADDRESS__ 0
|
|
||||||
#define __ACCESS_TABLE_INC__ "secmon_emc01_access_table_data.inc"
|
|
||||||
|
|
||||||
#include "secmon_define_access_table.inc"
|
#include "../pcv.hpp"
|
||||||
|
|
||||||
#undef __ACCESS_TABLE_INC__
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
#undef __ACCESS_TABLE_ADDRESS__
|
|
||||||
#undef __ACCESS_TABLE_NAME__
|
Result CpuFreqVdd(u32 *ptr);
|
||||||
|
Result CpuVoltDVFS(u32 *ptr);
|
||||||
|
Result CpuVoltThermals(u32 *ptr);
|
||||||
|
Result CpuVoltDfll(u32 *ptr);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../pcv.hpp"
|
||||||
|
#include "../pcv_asm.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
|
|
||||||
|
Result GpuVoltDVFS(u32 *ptr) {
|
||||||
|
/* Check for valid pattern. */
|
||||||
|
for (size_t i = 0; i < std::size(gpuDVFSPattern); ++i) {
|
||||||
|
if (*(ptr + i + 1) != gpuDVFSPattern[i]) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default value is 1050mV. */
|
||||||
|
if (C.marikoGpuVmax) {
|
||||||
|
PATCH_OFFSET(ptr + 1, C.marikoGpuVmax);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C.marikoGpuVmin) {
|
||||||
|
PATCH_OFFSET(ptr, C.marikoGpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GpuVoltThermals(u32 *ptr) {
|
||||||
|
if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuDvfs());
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (C.marikoGpuBootVolt) {
|
||||||
|
// PATCH_OFFSET(ptr - 3, C.marikoGpuBootVolt);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (C.marikoGpuVmin) {
|
||||||
|
PATCH_OFFSET(ptr, C.marikoGpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 3, C.marikoGpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 6, C.marikoGpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 9, C.marikoGpuVmin);
|
||||||
|
PATCH_OFFSET(ptr + 12, C.marikoGpuVmin);
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GpuFreqMaxAsm(u32 *ptr32) {
|
||||||
|
// Check if both two instructions match the pattern
|
||||||
|
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
|
||||||
|
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both instructions should operate on the same register
|
||||||
|
u8 rd = asm_get_rd(ins1);
|
||||||
|
if (rd != asm_get_rd(ins2)) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 max_clock;
|
||||||
|
switch (C.marikoGpuUV) {
|
||||||
|
case 0:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT15)->freq;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHighUV)->freq;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 asm_patch[2] = {
|
||||||
|
asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd),
|
||||||
|
asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)
|
||||||
|
};
|
||||||
|
|
||||||
|
PATCH_OFFSET(ptr32, asm_patch[0]);
|
||||||
|
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GpuFreqPllMax(u32 *ptr) {
|
||||||
|
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
|
||||||
|
|
||||||
|
// All zero except for freq
|
||||||
|
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) {
|
||||||
|
R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double the max clk simply
|
||||||
|
u32 max_clk = entry->freq * 2;
|
||||||
|
entry->freq = max_clk;
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GpuFreqPllLimit(u32 *ptr) {
|
||||||
|
u32 prev_freq = *(ptr - 1);
|
||||||
|
|
||||||
|
if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) {
|
||||||
|
R_THROW(ldr::ResultInvalidGpuPllEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
PATCH_OFFSET(ptr, 3600000);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../pcv.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
|
|
||||||
|
Result GpuVoltDVFS(u32 *ptr);
|
||||||
|
Result GpuVoltThermals(u32 *ptr);
|
||||||
|
Result GpuFreqMaxAsm(u32 *ptr32);
|
||||||
|
Result GpuFreqPllMax(u32 *ptr);
|
||||||
|
Result GpuFreqPllLimit(u32 *ptr);
|
||||||
|
|
||||||
|
}
|
||||||
+49
-560
@@ -20,333 +20,14 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <vector>
|
#include "../pcv.hpp"
|
||||||
#include "pcv.hpp"
|
#include "../../mtc_timing_value.hpp"
|
||||||
#include "../mtc_timing_value.hpp"
|
#include "calculate_timings_mariko.hpp"
|
||||||
#include "../mariko/calculate_timings.hpp"
|
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::mariko {
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
|
|
||||||
Result GpuVoltDVFS(u32 *ptr) {
|
namespace {
|
||||||
/* Check for valid pattern. */
|
std::vector<u32> newEmcList;
|
||||||
for (size_t i = 0; i < std::size(gpuDVFSPattern); ++i) {
|
|
||||||
if (*(ptr + i + 1) != gpuDVFSPattern[i]) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default value is 1050mV. */
|
|
||||||
if (C.marikoGpuVmax) {
|
|
||||||
PATCH_OFFSET(ptr + 1, C.marikoGpuVmax);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.marikoGpuVmin) {
|
|
||||||
PATCH_OFFSET(ptr, C.marikoGpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GpuVoltThermals(u32 *ptr) {
|
|
||||||
if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuDvfs());
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (C.marikoGpuBootVolt) {
|
|
||||||
// PATCH_OFFSET(ptr - 3, C.marikoGpuBootVolt);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (C.marikoGpuVmin) {
|
|
||||||
PATCH_OFFSET(ptr, C.marikoGpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 3, C.marikoGpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 6, C.marikoGpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 9, C.marikoGpuVmin);
|
|
||||||
PATCH_OFFSET(ptr + 12, C.marikoGpuVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 CapCpuClock() {
|
|
||||||
u32 cpuCap = allowedCpuMaxFrequencies[0];
|
|
||||||
|
|
||||||
for (u32 freq : allowedCpuMaxFrequencies) {
|
|
||||||
if (C.marikoCpuMaxClock >= freq) {
|
|
||||||
cpuCap = freq;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cpuCap;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CpuFreqVdd(u32 *ptr) {
|
|
||||||
dvfs_rail *entry = reinterpret_cast<dvfs_rail *>(reinterpret_cast<u8 *>(ptr) - offsetof(dvfs_rail, freq));
|
|
||||||
|
|
||||||
R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry());
|
|
||||||
R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry());
|
|
||||||
R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry());
|
|
||||||
R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry());
|
|
||||||
|
|
||||||
if (C.marikoCpuUVHigh) {
|
|
||||||
PATCH_OFFSET(ptr, CapCpuClock());
|
|
||||||
} else {
|
|
||||||
PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CpuVoltDVFS(u32 *ptr) {
|
|
||||||
CvbMeta *cpuCvbMeta = reinterpret_cast<CvbMeta *>(reinterpret_cast<u8 *>(ptr) - offsetof(CvbMeta, vmin));
|
|
||||||
|
|
||||||
R_UNLESS(cpuCvbMeta->highVmin == CpuHighVminOfficial, ldr::ResultInvalidCpuMinVolt());
|
|
||||||
R_UNLESS(cpuCvbMeta->unkStepMaybe == 38, ldr::ResultInvalidCpuMinVolt());
|
|
||||||
R_UNLESS(cpuCvbMeta->vmax == CpuVoltOfficial, ldr::ResultInvalidCpuMinVolt());
|
|
||||||
R_UNLESS(cpuCvbMeta->unkScale2 == 1000, ldr::ResultInvalidCpuMinVolt());
|
|
||||||
R_UNLESS(cpuCvbMeta->speedoScale == 100, ldr::ResultInvalidCpuMinVolt());
|
|
||||||
R_UNLESS(cpuCvbMeta->voltageScale == 1000, ldr::ResultInvalidCpuMinVolt());
|
|
||||||
R_UNLESS(cpuCvbMeta->unkZero5 == 0, ldr::ResultInvalidCpuMinVolt());
|
|
||||||
|
|
||||||
if (C.marikoCpuLowVmin) {
|
|
||||||
PATCH_OFFSET(&(cpuCvbMeta->vmin), C.marikoCpuLowVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.marikoCpuHighVmin) {
|
|
||||||
PATCH_OFFSET(&(cpuCvbMeta->highVmin), C.marikoCpuHighVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.marikoCpuMaxVolt) {
|
|
||||||
PATCH_OFFSET(&(cpuCvbMeta->vmax), C.marikoCpuMaxVolt);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CpuVoltThermals(u32 *ptr) {
|
|
||||||
if (std::memcmp(ptr, cpuVoltThermalData, sizeof(cpuVoltThermalData))) {
|
|
||||||
R_THROW(ldr::ResultInvalidCpuMinVolt());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.marikoCpuLowVmin) {
|
|
||||||
PATCH_OFFSET(ptr, C.marikoCpuLowVmin);
|
|
||||||
PATCH_OFFSET(ptr + 3, C.marikoCpuLowVmin);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C.marikoCpuMaxVolt) {
|
|
||||||
PATCH_OFFSET(ptr - 2, C.marikoCpuMaxVolt);
|
|
||||||
PATCH_OFFSET(ptr - 5, C.marikoCpuMaxVolt);
|
|
||||||
PATCH_OFFSET(ptr + 1, C.marikoCpuMaxVolt);
|
|
||||||
PATCH_OFFSET(ptr + 4, C.marikoCpuMaxVolt);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result CpuVoltDfll(u32 *ptr) {
|
|
||||||
CvbCpuDfllData *entry = reinterpret_cast<CvbCpuDfllData *>(ptr);
|
|
||||||
|
|
||||||
R_UNLESS(entry->tune0_low == 0xFFCF, ldr::ResultInvalidCpuVoltDfllEntry());
|
|
||||||
R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry());
|
|
||||||
R_UNLESS(entry->tune1_low == 0x12207FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
|
||||||
R_UNLESS(entry->tune1_high == 0x3FFF7FF, ldr::ResultInvalidCpuVoltDfllEntry());
|
|
||||||
|
|
||||||
switch (C.marikoCpuUVLow) {
|
|
||||||
case 1:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_low), 0xffa0);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21107ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x0);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21107ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27207ff);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_low), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21107ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27307ff);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_low), 0xffff);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21107ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27407ff);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21607ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27707ff);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21607ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27807ff);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21607ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff);
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff);
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27c07ff);
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27d07ff);
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27e07ff);
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_low), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_low), 0x21707ff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27f07ff);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (C.marikoCpuUVHigh) {
|
|
||||||
case 1:
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x0);
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffff);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27207ff);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27307ff);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27407ff);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27707ff);
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xffdf);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27807ff);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
case 8:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff);
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27c07ff);
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27d07ff);
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27e07ff);
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
PATCH_OFFSET(&(entry->tune0_high), 0xdfff);
|
|
||||||
PATCH_OFFSET(&(entry->tune1_high), 0x27f07ff);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GpuFreqMaxAsm(u32 *ptr32) {
|
|
||||||
// Check if both two instructions match the pattern
|
|
||||||
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
|
|
||||||
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both instructions should operate on the same register
|
|
||||||
u8 rd = asm_get_rd(ins1);
|
|
||||||
if (rd != asm_get_rd(ins2)) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 max_clock;
|
|
||||||
switch (C.marikoGpuUV) {
|
|
||||||
case 0:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT15)->freq;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHighUV)->freq;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 asm_patch[2] = {
|
|
||||||
asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd),
|
|
||||||
asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)
|
|
||||||
};
|
|
||||||
|
|
||||||
PATCH_OFFSET(ptr32, asm_patch[0]);
|
|
||||||
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GpuFreqPllMax(u32 *ptr) {
|
|
||||||
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
|
|
||||||
|
|
||||||
// All zero except for freq
|
|
||||||
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) {
|
|
||||||
R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double the max clk simply
|
|
||||||
u32 max_clk = entry->freq * 2;
|
|
||||||
entry->freq = max_clk;
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result GpuFreqPllLimit(u32 *ptr) {
|
|
||||||
u32 prev_freq = *(ptr - 1);
|
|
||||||
|
|
||||||
if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) {
|
|
||||||
R_THROW(ldr::ResultInvalidGpuPllEntry());
|
|
||||||
}
|
|
||||||
|
|
||||||
PATCH_OFFSET(ptr, 3600000);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemMtcTableAutoAdjust(MarikoMtcTable *table) {
|
void MemMtcTableAutoAdjust(MarikoMtcTable *table) {
|
||||||
@@ -617,11 +298,6 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::vector<u32> newEmcList;
|
|
||||||
u32 *nsoStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MtcGenerateJedecTable() {
|
void MtcGenerateJedecTable() {
|
||||||
const u32 jedecFreqs[] = { 1866000, 1996000, 2133000, 2400000, 2666000, 2933000, 3200000 };
|
const u32 jedecFreqs[] = { 1866000, 1996000, 2133000, 2400000, 2666000, 2933000, 3200000 };
|
||||||
constexpr u32 JedecFreqCount = std::size(jedecFreqs);
|
constexpr u32 JedecFreqCount = std::size(jedecFreqs);
|
||||||
@@ -638,7 +314,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
|
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
|
||||||
}
|
}
|
||||||
|
|
||||||
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
|
newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MtcGenerate133StepTable() {
|
void MtcGenerate133StepTable() {
|
||||||
@@ -657,12 +333,39 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
|
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
|
||||||
}
|
}
|
||||||
|
|
||||||
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
|
newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MtcGenerate33StepTable() {
|
||||||
|
/* ~33.33MHz but rounded*/
|
||||||
|
const u32 stepFreqs33[] = {
|
||||||
|
1633000, 1666000, 1700000, 1733000, 1766000, 1800000, 1833000, 1866000, 1900000, 1933000,
|
||||||
|
1966000, 2000000, 2033000, 2066000, 2100000, 2133000, 2166000, 2200000, 2233000, 2266000,
|
||||||
|
2300000, 2333000, 2366000, 2400000, 2433000, 2466000, 2500000, 2533000, 2566000, 2600000,
|
||||||
|
2633000, 2666000, 2700000, 2733000, 2766000, 2800000, 2833000, 2866000, 2900000, 2933000,
|
||||||
|
2966000, 3000000, 3033000, 3066000, 3100000, 3133000, 3166000, 3200000, 3233000, 3266000,
|
||||||
|
3300000, 3333000, 3366000, 3400000, 3433000, 3466000, 3500000,
|
||||||
|
};
|
||||||
|
constexpr u32 StepFreqs33Size = std::size(stepFreqs33);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < StepFreqs33Size; ++i) {
|
||||||
|
if (stepFreqs33[i] <= C.marikoEmcMaxClock) {
|
||||||
|
newEmcList.push_back(stepFreqs33[i]);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newEmcList.back() != C.marikoEmcMaxClock) {
|
||||||
|
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
|
||||||
|
}
|
||||||
|
|
||||||
|
newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MtcGenerateFreqTables() {
|
void MtcGenerateFreqTables() {
|
||||||
newEmcList.clear();
|
newEmcList.clear();
|
||||||
newEmcList.reserve(DvfsTableEntryCount);
|
newEmcList.reserve(EmcDvfsTableEntryCount);
|
||||||
newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault));
|
newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault));
|
||||||
|
|
||||||
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
|
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
|
||||||
@@ -671,6 +374,9 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
|
|
||||||
u32 stepRate = 0;
|
u32 stepRate = 0;
|
||||||
switch (C.stepMode) {
|
switch (C.stepMode) {
|
||||||
|
case StepMode_33MHz:
|
||||||
|
MtcGenerate33StepTable();
|
||||||
|
return;
|
||||||
case StepMode_66MHz:
|
case StepMode_66MHz:
|
||||||
stepRate = 66667;
|
stepRate = 66667;
|
||||||
break;
|
break;
|
||||||
@@ -701,7 +407,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
newEmcList.push_back(newFreq);
|
newEmcList.push_back(newFreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
|
newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) {
|
Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) {
|
||||||
@@ -769,6 +475,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
Result MemFreqMtcTable(u32 *ptr) {
|
Result MemFreqMtcTable(u32 *ptr) {
|
||||||
static const DramId dramId = [] {
|
static const DramId dramId = [] {
|
||||||
DramId id = GetDramId();
|
DramId id = GetDramId();
|
||||||
|
id = HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF;
|
||||||
return id;
|
return id;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
@@ -905,24 +612,23 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
#undef DVB
|
#undef DVB
|
||||||
#undef DVB_OC
|
#undef DVB_OC
|
||||||
|
|
||||||
DvbEntry emcDvbTableOc[newEmcList.size()];
|
const size_t dvbCount = std::min(newEmcList.size(), DvbTableCapacity);
|
||||||
|
DvbEntry emcDvbTableOc[DvbTableCapacity] = {};
|
||||||
|
|
||||||
u32 bracketIndex = 0;
|
u32 bracketIndex = 0;
|
||||||
for (u32 i = 0; i < newEmcList.size(); ++i) {
|
for (size_t i = 0; i < dvbCount; ++i) {
|
||||||
while (newEmcList[i] >= emcDvbOcTableBrackets[bracketIndex + 1].freq) {
|
const u32 freq = (i == dvbCount - 1) ? static_cast<u32>(newEmcList.back()) : newEmcList[i];
|
||||||
|
while (freq >= emcDvbOcTableBrackets[bracketIndex + 1].freq) {
|
||||||
++bracketIndex;
|
++bracketIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
emcDvbTableOc[i].freq = newEmcList[i];
|
emcDvbTableOc[i].freq = freq;
|
||||||
std::memcpy(emcDvbTableOc[i].volt, emcDvbOcTableBrackets[bracketIndex].volt, sizeof(emcDvbTableOc[i].volt));
|
std::memcpy(emcDvbTableOc[i].volt, emcDvbOcTableBrackets[bracketIndex].volt, sizeof(emcDvbTableOc[i].volt));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memset(mem_dvb_table_head, 0, sizeof(EmcDvbTableDefault));
|
/* Clear the entire 32-entry region */
|
||||||
std::memcpy(mem_dvb_table_head, &emcDvbTableOc, sizeof(emcDvbTableOc));
|
std::memset(mem_dvb_table_head, 0, DvbTableCapacity * sizeof(DvbEntry));
|
||||||
|
std::memcpy(mem_dvb_table_head, emcDvbTableOc, dvbCount * sizeof(DvbEntry));
|
||||||
/* Max dvfs entry is 32, but HOS doesn't seem to boot if exact freq doesn't exist in dvb table,
|
|
||||||
reason why it's like this
|
|
||||||
*/
|
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -936,57 +642,6 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) {
|
|
||||||
struct {
|
|
||||||
u8 reg;
|
|
||||||
u8 val;
|
|
||||||
} __attribute__((packed)) cmd;
|
|
||||||
|
|
||||||
I2cSession _session;
|
|
||||||
R_TRY(i2cOpenSession(&_session, dev));
|
|
||||||
|
|
||||||
cmd.reg = reg;
|
|
||||||
cmd.val = val;
|
|
||||||
Result res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
|
|
||||||
i2csessionClose(&_session);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result EmcVddqVolt(u32 *ptr) {
|
|
||||||
regulator *entry = reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_2_3.default_uv));
|
|
||||||
|
|
||||||
constexpr u32 uv_step = 5'000;
|
|
||||||
constexpr u32 uv_min = 250'000;
|
|
||||||
|
|
||||||
auto validator = [entry]() {
|
|
||||||
R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
|
|
||||||
R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
|
|
||||||
R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
|
|
||||||
R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
|
|
||||||
R_SUCCEED();
|
|
||||||
};
|
|
||||||
|
|
||||||
R_TRY(validator());
|
|
||||||
|
|
||||||
u32 emc_uv = C.marikoEmcVddqVolt;
|
|
||||||
|
|
||||||
if (!emc_uv) {
|
|
||||||
R_SKIP();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emc_uv % uv_step) {
|
|
||||||
emc_uv = (emc_uv + uv_step - 1) / uv_step * uv_step; // rounding
|
|
||||||
}
|
|
||||||
|
|
||||||
PATCH_OFFSET(ptr, emc_uv);
|
|
||||||
|
|
||||||
i2cInitialize();
|
|
||||||
Result resultI2C = I2cSet_U8(I2cDevice_Max77812_2, 0x25, (emc_uv - uv_min) / uv_step);
|
|
||||||
i2cExit();
|
|
||||||
|
|
||||||
return resultI2C;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result MemMtcTableAsm(u32 *ptr) {
|
Result MemMtcTableAsm(u32 *ptr) {
|
||||||
constexpr u32 AddpOffset = 1;
|
constexpr u32 AddpOffset = 1;
|
||||||
constexpr u32 BrOffset = 12;
|
constexpr u32 BrOffset = 12;
|
||||||
@@ -1018,170 +673,4 @@ namespace ams::ldr::hoc::pcv::mariko {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetSocSpeedo(u32 &socSpeedo) {
|
|
||||||
constexpr u64 FusePhysicalAddress = 0x7000F000;
|
|
||||||
u64 virtualAddress = 0;
|
|
||||||
constexpr u64 Size = 0x1000;
|
|
||||||
|
|
||||||
u64 outSize;
|
|
||||||
/* TODO: use svc::QueryMemoryMapping instead. */
|
|
||||||
R_TRY(svcQueryMemoryMapping(&virtualAddress, &outSize, FusePhysicalAddress, Size));
|
|
||||||
|
|
||||||
constexpr u32 FuseOffset = 2048;
|
|
||||||
constexpr u32 SocSpeedoOffset = 308;
|
|
||||||
socSpeedo = *reinterpret_cast<u32 *>(virtualAddress + FuseOffset + SocSpeedoOffset);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetSocProcessId(u32 socSpeedo) {
|
|
||||||
if (socSpeedo <= 1597) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (socSpeedo <= 1708) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* >= 1709. */
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result SocVoltAsm(u32 *compareSpeedos) {
|
|
||||||
constexpr u32 VoltageScanLimit = 10;
|
|
||||||
/* Might actually be speedo id. */
|
|
||||||
u32 *writeProcessId = ScanAssembly(compareSpeedos, VoltageScanLimit, SocVoltWriteProcessIdAsm, asm_compare_no_rd);
|
|
||||||
R_UNLESS(writeProcessId != nullptr, ldr::ResultInvalidSocVoltPattern());
|
|
||||||
u8 writeProcessIdRd = asm_get_rd(*writeProcessId);
|
|
||||||
|
|
||||||
/* This writes 1050mV. */
|
|
||||||
u32 *writeVoltage = ScanAssembly(writeProcessId, VoltageScanLimit, SocVoltWriteVoltageAsm, asm_compare_no_rd);
|
|
||||||
R_UNLESS(writeVoltage != nullptr, ldr::ResultInvalidSocVoltPattern());
|
|
||||||
u8 writeVoltageRd = asm_get_rd(*writeVoltage);
|
|
||||||
|
|
||||||
/* A csel instruction is used to select the soc voltage limit register. */
|
|
||||||
/* We care about its destination register since that is used for verification. */
|
|
||||||
constexpr u32 VoltageSelectScanLimit = 24;
|
|
||||||
u32 *selectVoltage = ScanAssembly(writeVoltage, VoltageSelectScanLimit, SocVoltSelectRegisterAsm, AsmCompareCselNoReg);
|
|
||||||
R_UNLESS(selectVoltage != nullptr, ldr::ResultInvalidSocVoltPattern());
|
|
||||||
/* Todo: check rm and rn? */
|
|
||||||
u8 selectVoltageRd = asm_get_rd(*selectVoltage);
|
|
||||||
|
|
||||||
/* rdCsel is then multiplied by 1000 to convert to uV. */
|
|
||||||
/* This is pretty far down the function. */
|
|
||||||
constexpr u32 MultiplierScanLimit = 200;
|
|
||||||
u32 *multiplier = ScanAssembly(selectVoltage, MultiplierScanLimit, SocVoltMultiplyVoltsAsm, AsmCompareMullNoReg);
|
|
||||||
R_UNLESS(multiplier != nullptr, ldr::ResultInvalidSocVoltPattern());
|
|
||||||
u8 multiplierRn = AsmGetMullRn(*multiplier);
|
|
||||||
u8 multiplierRm = AsmGetMullRm(*multiplier);
|
|
||||||
/* One of the two registers has to be rdCsel. */
|
|
||||||
R_UNLESS((multiplierRn == selectVoltageRd) || (multiplierRm == selectVoltageRd), ldr::ResultInvalidSocVoltPattern());
|
|
||||||
u8 multiplierRd = asm_get_rd(*multiplier);
|
|
||||||
|
|
||||||
/* Subs instruction is then used to verify against absolute limit. */
|
|
||||||
u32 limitValidationPattern = AsmSubsSetRn(SocVoltValidateLimitAsm, multiplierRd);
|
|
||||||
u32 *limitValidation = ScanAssembly(multiplier, VoltageScanLimit, limitValidationPattern, AsmSubsCompareNoReg);
|
|
||||||
R_UNLESS(limitValidation != nullptr, ldr::ResultInvalidSocVoltPattern());
|
|
||||||
|
|
||||||
/* There is a b.gt instruction right after (checks for socVoltageCap < socVoltageMax). */
|
|
||||||
u32 *branchToAbort = limitValidation + 1;
|
|
||||||
R_UNLESS(AsmCompareBrConNoImm19(*branchToAbort, SocVoltBranchToAbortAsm), ldr::ResultInvalidSocVoltPattern());
|
|
||||||
|
|
||||||
if (!C.marikoSocVmax || C.marikoSocVmax <= 1000) {
|
|
||||||
R_SKIP();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adjust 1598 speedo minimum to ensure it always goes down process id 0 branch. */
|
|
||||||
/* 2200 should be high enough :D */
|
|
||||||
u32 compareSpeedosPatch = AsmSubsSetImm12(*compareSpeedos, 2200);
|
|
||||||
PATCH_OFFSET(compareSpeedos, compareSpeedosPatch);
|
|
||||||
|
|
||||||
u32 socSpeedo = 0;
|
|
||||||
R_TRY(GetSocSpeedo(socSpeedo));
|
|
||||||
|
|
||||||
/* Adjust processId from 0 to [process id of switch booting this]. */
|
|
||||||
/* We're overwriting the orr instruction entirly. */
|
|
||||||
u32 processId = GetSocProcessId(socSpeedo);
|
|
||||||
u32 writeProcessIdPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, processId), writeProcessIdRd);
|
|
||||||
PATCH_OFFSET(writeProcessId, writeProcessIdPatch);
|
|
||||||
|
|
||||||
/* Adjust voltage limit. */
|
|
||||||
u32 voltageLimitPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, C.marikoSocVmax), writeVoltageRd);
|
|
||||||
PATCH_OFFSET(writeVoltage, voltageLimitPatch);
|
|
||||||
|
|
||||||
/* Branches to an abort if limits are invalid -- we patch the branch instruction with NOP. */
|
|
||||||
PATCH_OFFSET(branchToAbort, NopIns);
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result SocVoltLimit(u32 *ptr) {
|
|
||||||
R_UNLESS(!std::memcmp(ptr - SocVoltLimitMaxDefaultIndex, socVoltLimitArray, sizeof(socVoltLimitArray)), ldr::ResultInvalidSocVoltLimit());
|
|
||||||
if (!C.marikoSocVmax || C.marikoSocVmax <= SocVoltLimitOfficial) {
|
|
||||||
R_SKIP();
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr u32 Step = 25;
|
|
||||||
u32 maxVolt = C.marikoSocVmax;
|
|
||||||
if (maxVolt % Step) {
|
|
||||||
maxVolt = maxVolt / Step * Step; /* Round. */
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 volt = SocVoltLimitOfficial;
|
|
||||||
for (u32 i = 1; i < DvfsTableEntryCount - SocVoltLimitMaxDefaultIndex && volt < maxVolt; ++i) {
|
|
||||||
volt += Step;
|
|
||||||
PATCH_OFFSET(ptr + i, volt);
|
|
||||||
}
|
|
||||||
|
|
||||||
R_SUCCEED();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
|
||||||
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
|
||||||
MtcGenerateFreqTables();
|
|
||||||
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
|
||||||
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
|
|
||||||
|
|
||||||
PatcherEntry<u32> patches[] = {
|
|
||||||
{ "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit },
|
|
||||||
{ "CPU Freq Table", CpuFreqCvbTable<true>, 1, nullptr, CpuCvbDefaultMaxFreq },
|
|
||||||
{ "CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial },
|
|
||||||
{ "CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial },
|
|
||||||
{ "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low },
|
|
||||||
{ "GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial },
|
|
||||||
{ "GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
|
|
||||||
{ "GPU Freq Table", GpuFreqCvbTable<true>, 1, nullptr, GpuCvbDefaultMaxFreq },
|
|
||||||
{ "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
|
|
||||||
{ "GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
|
|
||||||
{ "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
|
|
||||||
{ "MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
|
|
||||||
{ "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit },
|
|
||||||
{ "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
|
|
||||||
{ "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
|
|
||||||
{ "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault },
|
|
||||||
{ "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default },
|
|
||||||
{ "MEM Table Asm", &MemMtcTableAsm, 1, &MemMtcGetGetTablePatternFn },
|
|
||||||
{ "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn },
|
|
||||||
{ "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) {
|
|
||||||
u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
|
|
||||||
for (auto &entry : patches) {
|
|
||||||
if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &entry : patches) {
|
|
||||||
LOGGING("%s Count: %zu\n", entry.description, entry.patched_count);
|
|
||||||
if (R_FAILED(entry.CheckResult())) {
|
|
||||||
panic::SmcError(panic::Patch);
|
|
||||||
|
|
||||||
CRASH(entry.description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) Switch-OC-Suite
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 hanai3Bi
|
||||||
|
*
|
||||||
|
* Copyright (c) B3711
|
||||||
|
*
|
||||||
|
* Copyright (c) Souldbminer and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../pcv.hpp"
|
||||||
|
|
||||||
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
|
|
||||||
|
void MtcGenerateFreqTables();
|
||||||
|
Result MemFreqMtcTable(u32 *ptr);
|
||||||
|
Result MemFreqDvbTable(u32 *ptr);
|
||||||
|
Result MemFreqMax(u32 *ptr);
|
||||||
|
Result MemMtcTableAsm(u32 *ptr);
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -14,7 +14,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "../oc_common.hpp"
|
#include "../../oc_common.hpp"
|
||||||
#include "timing_tables.hpp"
|
#include "timing_tables.hpp"
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::mariko {
|
namespace ams::ldr::hoc::pcv::mariko {
|
||||||
File renamed without changes.
@@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "pcv.hpp"
|
#include "pcv.hpp"
|
||||||
#include "pcv_hook.hpp"
|
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv {
|
namespace ams::ldr::hoc::pcv {
|
||||||
|
|
||||||
@@ -28,8 +27,8 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry());
|
R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry());
|
||||||
|
|
||||||
// Double the max clk simply
|
// Double the max clk simply
|
||||||
u32 max_clk = entry->freq * 2;
|
u32 max_clk = entry->freq * 2;
|
||||||
entry->freq = max_clk;
|
entry->freq = max_clk;
|
||||||
entry->vco_max = max_clk;
|
entry->vco_max = max_clk;
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -42,7 +41,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
};
|
};
|
||||||
|
|
||||||
constexpr u32 uv_step = 12'500;
|
constexpr u32 uv_step = 12'500;
|
||||||
constexpr u32 uv_min = 600'000;
|
constexpr u32 uv_min = 600'000;
|
||||||
|
|
||||||
auto validator = [](regulator *entry) {
|
auto validator = [](regulator *entry) {
|
||||||
R_UNLESS(entry->id == 1, ldr::ResultInvalidRegulatorEntry());
|
R_UNLESS(entry->id == 1, ldr::ResultInvalidRegulatorEntry());
|
||||||
@@ -68,7 +67,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (emc_uv % uv_step) {
|
if (emc_uv % uv_step) {
|
||||||
emc_uv = emc_uv / uv_step * uv_step; // rounding
|
emc_uv = emc_uv / uv_step * uv_step; // rounding
|
||||||
}
|
}
|
||||||
|
|
||||||
PATCH_OFFSET(ptr, emc_uv);
|
PATCH_OFFSET(ptr, emc_uv);
|
||||||
@@ -103,11 +102,12 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
|
|
||||||
u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
|
u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
|
||||||
u32 marikoCpuDvfsMaxFreq;
|
u32 marikoCpuDvfsMaxFreq;
|
||||||
if (C.marikoCpuUVHigh) {
|
if (C.marikoCpuUVHigh) {
|
||||||
marikoCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq);
|
marikoCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq);
|
||||||
} else {
|
} else {
|
||||||
marikoCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
|
marikoCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 eristaGpuDvfsMaxFreq;
|
u32 eristaGpuDvfsMaxFreq;
|
||||||
switch (C.eristaGpuUV) {
|
switch (C.eristaGpuUV) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -148,11 +148,11 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
|
|
||||||
Validator validators[] = {
|
Validator validators[] = {
|
||||||
{ C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true },
|
{ C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true },
|
||||||
{ C.marikoCpuBoostClock, 1020'000, 2805'000, panic::Cpu, true },
|
{ C.marikoCpuBoostClock, 1020'000, 2703'000, panic::Cpu, true },
|
||||||
{ C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, },
|
{ C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, },
|
||||||
{ C.marikoCpuMaxVolt, 1000, 1235, panic::Cpu, },
|
{ C.marikoCpuMaxVolt, 1000, 1200, panic::Cpu, },
|
||||||
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, },
|
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, },
|
||||||
{ marikoCpuDvfsMaxFreq, 1785'000, 2805'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.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, }, /* Official vmax for the RAMs is 1400-1500mV */
|
||||||
{ C.eristaEmcMaxClock, 1600'000, 2600'000, panic::Emc, },
|
{ C.eristaEmcMaxClock, 1600'000, 2600'000, panic::Emc, },
|
||||||
{ C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, },
|
{ C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, },
|
||||||
@@ -172,7 +172,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WriteKipLoadToIram() {
|
void WriteKipLoadToIram() {
|
||||||
const u32 hocMagic = 0x686F634D;
|
const u32 hocMagic = 0x686F634D;
|
||||||
constexpr uintptr_t LoadMagicAddress = 0x4003DC00; /* Should be a pretty safe address. */
|
constexpr uintptr_t LoadMagicAddress = 0x4003DC00; /* Should be a pretty safe address. */
|
||||||
R_DISCARD(SmcCopyToIram(LoadMagicAddress, &hocMagic, sizeof(hocMagic)));
|
R_DISCARD(SmcCopyToIram(LoadMagicAddress, &hocMagic, sizeof(hocMagic)));
|
||||||
}
|
}
|
||||||
@@ -182,6 +182,9 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
|
|
||||||
Hooks().Initialize(mapped_nso, nso_address, cave, cave_size, data_arena);
|
Hooks().Initialize(mapped_nso, nso_address, cave, cave_size, data_arena);
|
||||||
|
|
||||||
|
g_pcv_cave = cave;
|
||||||
|
g_pcv_cave_size = cave_size;
|
||||||
|
|
||||||
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
|
||||||
if (isMariko) {
|
if (isMariko) {
|
||||||
mariko::Patch(mapped_nso, nso_size);
|
mariko::Patch(mapped_nso, nso_size);
|
||||||
|
|||||||
@@ -22,8 +22,14 @@
|
|||||||
|
|
||||||
#include "../oc_common.hpp"
|
#include "../oc_common.hpp"
|
||||||
#include "pcv_common.hpp"
|
#include "pcv_common.hpp"
|
||||||
#include "pcv_erista.hpp"
|
|
||||||
#include "pcv_mariko.hpp"
|
#include "erista/pcv_erista_cpu.hpp"
|
||||||
|
#include "erista/pcv_erista_gpu.hpp"
|
||||||
|
#include "erista/pcv_erista_mtc.hpp"
|
||||||
|
#include "erista/pcv_erista.hpp"
|
||||||
|
|
||||||
|
#include "mariko/pcv_mariko.hpp"
|
||||||
|
|
||||||
#include "pcv_hook.hpp"
|
#include "pcv_hook.hpp"
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv {
|
namespace ams::ldr::hoc::pcv {
|
||||||
@@ -137,7 +143,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
default:
|
default:
|
||||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
|
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (C.eristaGpuUV) {
|
switch (C.eristaGpuUV) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -152,7 +158,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
default:
|
default:
|
||||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t default_entry_count = GetDvfsTableEntryCount(default_table);
|
size_t default_entry_count = GetDvfsTableEntryCount(default_table);
|
||||||
@@ -188,6 +194,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
}
|
}
|
||||||
++entry;
|
++entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (C.commonGpuVoltOffset && !(isMariko ? C.marikoGpuUV : C.eristaGpuUV)) {
|
if (C.commonGpuVoltOffset && !(isMariko ? C.marikoGpuUV : C.eristaGpuUV)) {
|
||||||
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
|
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
|
||||||
for (size_t i = 0; i < customize_entry_count; ++i) {
|
for (size_t i = 0; i < customize_entry_count; ++i) {
|
||||||
@@ -202,6 +209,10 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
Result MemFreqPllmLimit(u32 *ptr);
|
Result MemFreqPllmLimit(u32 *ptr);
|
||||||
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
|
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
|
||||||
|
|
||||||
|
/* Extra pcv .bss */
|
||||||
|
constexpr size_t HocPcvScratchSize = 0x2000;
|
||||||
|
constexpr size_t HocBusFreqBufOffset = 0x1000; /* start of the SOC bus region */
|
||||||
|
|
||||||
void SafetyCheck();
|
void SafetyCheck();
|
||||||
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);
|
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);
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,15 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
return ins & ((1 << 5) - 1);
|
return ins & ((1 << 5) - 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Rn (bits 9:5) and Rm (bits 20:16) to get registers. */
|
||||||
|
inline auto AsmGetRn = [](u32 ins) -> u32 { return (ins >> 5) & 0x1Fu; };
|
||||||
|
inline auto AsmGetRm = [](u32 ins) -> u32 { return (ins >> 16) & 0x1Fu; };
|
||||||
|
|
||||||
|
/* Add (shifted register), 64-bit: sf=1 op=0 S=0 01011 shift(2) 0 Rm imm6 Rn Rd. */
|
||||||
|
inline auto AsmIsAddShiftedReg64 = [](u32 ins) {
|
||||||
|
return (ins & 0xFF200000u) == 0x8B000000u;
|
||||||
|
};
|
||||||
|
|
||||||
inline auto asm_set_rd = [](u32 ins, u8 rd) {
|
inline auto asm_set_rd = [](u32 ins, u8 rd) {
|
||||||
return (ins & 0xFFFFFFE0) | (rd & 0x1F);
|
return (ins & 0xFFFFFFE0) | (rd & 0x1F);
|
||||||
};
|
};
|
||||||
@@ -94,6 +103,16 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
return static_cast<uintptr_t>(static_cast<s64>(pc) + imm);
|
return static_cast<uintptr_t>(static_cast<s64>(pc) + imm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* adrp Rd, target */
|
||||||
|
inline auto AsmMakeAdrp = [](uintptr_t pc, uintptr_t target, u32 rd) -> u32 {
|
||||||
|
const s64 delta = static_cast<s64>(target & ~static_cast<uintptr_t>(0xFFF))
|
||||||
|
- static_cast<s64>(pc & ~static_cast<uintptr_t>(0xFFF));
|
||||||
|
const u32 imm = static_cast<u32>((delta >> 12) & 0x1FFFFF); /* 21-bit page */
|
||||||
|
const u32 immlo = imm & 0x3;
|
||||||
|
const u32 immhi = (imm >> 2) & 0x7FFFF;
|
||||||
|
return 0x90000000u | (immlo << 29) | (immhi << 5) | (rd & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
inline auto AsmSetAdrTarget = [](u32 ins, uintptr_t pc, uintptr_t target) -> u32 {
|
inline auto AsmSetAdrTarget = [](u32 ins, uintptr_t pc, uintptr_t target) -> u32 {
|
||||||
const s64 delta = static_cast<s64>(target) - static_cast<s64>(pc);
|
const s64 delta = static_cast<s64>(target) - static_cast<s64>(pc);
|
||||||
const u32 immlo = static_cast<u32>(delta & 0x3);
|
const u32 immlo = static_cast<u32>(delta & 0x3);
|
||||||
@@ -101,6 +120,167 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
return (ins & ~((0x3u << 29) | (0x7FFFFu << 5))) | (immlo << 29) | (immhi << 5);
|
return (ins & ~((0x3u << 29) | (0x7FFFFu << 5))) | (immlo << 29) | (immhi << 5);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* adrp: bit31=1, bits 28:24 = 10000. */
|
||||||
|
inline auto AsmIsAdrp = [](u32 ins) -> bool {
|
||||||
|
return (ins & 0x9F000000u) == 0x90000000u;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto AsmAdrpPageOffset = [](u32 ins) -> s64 {
|
||||||
|
s64 imm = static_cast<s64>((((ins >> 5) & 0x7FFFFu) << 2) | ((ins >> 29) & 0x3u));
|
||||||
|
imm = (imm << 43) >> 43; /* sign-extend the 21-bit immediate */
|
||||||
|
return imm << 12;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* add (immediate), 64-bit: sf=1 op=0 S=0 100010 sh imm12 Rn Rd. */
|
||||||
|
inline auto AsmIsAddImm64 = [](u32 ins) -> bool {
|
||||||
|
return (ins & 0xFF800000u) == 0x91000000u;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto AsmGetImm12 = [](u32 ins) -> u32 {
|
||||||
|
return (ins >> 10) & 0xFFFu;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto AsmMakeAddImm64 = [](u32 rd, u32 rn, u32 imm12) -> u32 {
|
||||||
|
return 0x91000000u | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* movz Wd,#imm16 (no shift). */
|
||||||
|
inline auto AsmMakeMovzW = [](u32 rd, u16 imm16) -> u32 {
|
||||||
|
return 0x52800000u | (static_cast<u32>(imm16) << 5) | (rd & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* mov Xd,Xm == orr Xd,XZR,Xm. */
|
||||||
|
inline auto AsmMakeMovReg = [](u32 rd, u32 rm) -> u32 {
|
||||||
|
return 0xAA0003E0u | ((rm & 0x1Fu) << 16) | (rd & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ldr Xt,[Xn,#byteOff] */
|
||||||
|
inline auto AsmMakeLdrImm64 = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||||
|
return 0xF9400000u | (((byteOff / 8u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* b <target> (PC-relative, +-128MB). */
|
||||||
|
inline auto AsmMakeB = [](uintptr_t pc, uintptr_t target) -> u32 {
|
||||||
|
const s64 off = (static_cast<s64>(target) - static_cast<s64>(pc)) >> 2;
|
||||||
|
return 0x14000000u | (static_cast<u32>(off) & 0x03FFFFFFu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* b.<cond> <target> (cond: LO/CC=0x3, LE=0xD, NE=0x1, ...). */
|
||||||
|
inline auto AsmMakeBCond = [](uintptr_t pc, uintptr_t target, u32 cond) -> u32 {
|
||||||
|
const s64 off = (static_cast<s64>(target) - static_cast<s64>(pc)) >> 2;
|
||||||
|
return 0x54000000u | ((static_cast<u32>(off) & 0x7FFFFu) << 5) | (cond & 0xFu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* sub Xd,Xn,#imm12 (shift 0). */
|
||||||
|
inline auto AsmMakeSubImm64 = [](u32 rd, u32 rn, u32 imm12) -> u32 {
|
||||||
|
return 0xD1000000u | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* cmp Wn,#imm12 == subs WZR,Wn,#imm12. */
|
||||||
|
inline auto AsmMakeCmpImm32 = [](u32 rn, u32 imm12) -> u32 {
|
||||||
|
return 0x7100001Fu | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* str Wt,[Xn,#byteOff] (32-bit, unsigned scaled by 4). */
|
||||||
|
inline auto AsmMakeStrImm32 = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||||
|
return 0xB9000000u | (((byteOff / 4u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ldr Wt,[Xn,#byteOff] (32-bit, unsigned scaled by 4). */
|
||||||
|
inline auto AsmMakeLdrImm32 = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||||
|
return 0xB9400000u | (((byteOff / 4u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* add Xd,Xn,Xm,LSL #shift (64-bit shifted register, shift 0-63). */
|
||||||
|
inline auto AsmMakeAddShiftedReg64 = [](u32 rd, u32 rn, u32 rm, u32 shift) -> u32 {
|
||||||
|
return 0x8B000000u | ((rm & 0x1Fu) << 16) | ((shift & 0x3Fu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* stp Xt1,Xt2,[Xn,#imm] (signed offset, scaled by 8). */
|
||||||
|
inline auto AsmMakeStpImm64 = [](u32 rt1, u32 rt2, u32 rn, s32 imm) -> u32 {
|
||||||
|
return 0xA9000000u | ((static_cast<u32>(imm / 8) & 0x7Fu) << 15) | ((rt2 & 0x1Fu) << 10) | ((rn & 0x1Fu) << 5) | (rt1 & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* stp Qt1,Qt2,[Xn,#imm] (128-bit SIMD, signed offset scaled by 16). */
|
||||||
|
inline auto AsmMakeStpqImm = [](u32 qt1, u32 qt2, u32 rn, s32 imm) -> u32 {
|
||||||
|
return 0xAD000000u | ((static_cast<u32>(imm / 16) & 0x7Fu) << 15) | ((qt2 & 0x1Fu) << 10) | ((rn & 0x1Fu) << 5) | (qt1 & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* str Xt,[Xn,#byteOff] (unsigned scaled by 8). */
|
||||||
|
inline auto AsmMakeStrImm64 = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||||
|
return 0xF9000000u | (((byteOff / 8u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* movn Wd,#imm16 (no shift): loads ~imm16, e.g. movn Wd,#0x37 == -56. */
|
||||||
|
inline auto AsmMakeMovnW = [](u32 rd, u16 imm16) -> u32 {
|
||||||
|
return 0x12800000u | (static_cast<u32>(imm16) << 5) | (rd & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* bl <target> (PC-relative, +-128MB). */
|
||||||
|
inline auto AsmMakeBl = [](uintptr_t pc, uintptr_t target) -> u32 {
|
||||||
|
const s64 off = (static_cast<s64>(target) - static_cast<s64>(pc)) >> 2;
|
||||||
|
return 0x94000000u | (static_cast<u32>(off) & 0x03FFFFFFu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* svc #imm16. */
|
||||||
|
inline auto AsmMakeSvc = [](u16 imm16) -> u32 {
|
||||||
|
return 0xD4000001u | (static_cast<u32>(imm16) << 5);
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u32 RetIns = 0xD65F03C0u; /* ret (x30) */
|
||||||
|
|
||||||
|
/* ldrb Wt,[Xn,#imm] (unsigned byte, scale 1). */
|
||||||
|
inline auto AsmMakeLdrbImm = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||||
|
return 0x39400000u | ((byteOff & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* cbz Wt,<target>. */
|
||||||
|
inline auto AsmMakeCbz = [](uintptr_t pc, uintptr_t target, u32 rt) -> u32 {
|
||||||
|
const s64 off = (static_cast<s64>(target) - static_cast<s64>(pc)) >> 2;
|
||||||
|
return 0x34000000u | ((static_cast<u32>(off) & 0x7FFFFu) << 5) | (rt & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* strb Wt,[Xn,#imm] (unsigned byte, scale 1). */
|
||||||
|
inline auto AsmMakeStrbImm = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||||
|
return 0x39000000u | ((byteOff & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* mov Xd,X<rm> == orr Xd,XZR,X<rm> (matches any Rd). */
|
||||||
|
inline auto AsmIsMovReg = [](u32 ins, u32 rm) -> bool {
|
||||||
|
return (ins & 0xFFFFFFE0u) == (0xAA0003E0u | ((rm & 0x1Fu) << 16));
|
||||||
|
};
|
||||||
|
|
||||||
|
/* add Xd,sp,#imm12 (shift 0). */
|
||||||
|
inline auto AsmIsAddSpImm = [](u32 ins) -> bool {
|
||||||
|
return (ins & 0xFFC003E0u) == 0x910003E0u;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* sub Xd,x29,#imm12 (shift 0). */
|
||||||
|
inline auto AsmIsSubX29Imm = [](u32 ins) -> bool {
|
||||||
|
return (ins & 0xFFC003E0u) == 0xD10003A0u;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto AsmIsB = [](u32 ins) -> bool { return (ins & 0xFC000000u) == 0x14000000u; }; /* b */
|
||||||
|
inline auto AsmIsBl = [](u32 ins) -> bool { return (ins & 0xFC000000u) == 0x94000000u; }; /* bl */
|
||||||
|
inline auto AsmIsBCond = [](u32 ins) -> bool { return (ins & 0xFF000010u) == 0x54000000u; }; /* b.c */
|
||||||
|
|
||||||
|
/* ldr/str Xt,[Xn,#imm] (64-bit, unsigned scaled offset). */
|
||||||
|
inline auto AsmIsLdrImm64 = [](u32 ins) -> bool { return (ins & 0xFFC00000u) == 0xF9400000u; };
|
||||||
|
inline auto AsmIsStrImm64 = [](u32 ins) -> bool { return (ins & 0xFFC00000u) == 0xF9000000u; };
|
||||||
|
inline auto AsmGetLdStImm64Off = [](u32 ins) -> u32 { return ((ins >> 10) & 0xFFFu) * 8u; };
|
||||||
|
|
||||||
|
/* Byte target address of a b/bl at pc. */
|
||||||
|
inline auto AsmBranchTarget = [](u32 ins, uintptr_t pc) -> uintptr_t {
|
||||||
|
s64 off = static_cast<s64>((ins & 0x03FFFFFFu) << 2);
|
||||||
|
off = (off << 36) >> 36; /* sign-extend the 28-bit branch offset */
|
||||||
|
return static_cast<uintptr_t>(static_cast<s64>(pc) + off);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Rewrite a scaled immediate-offset load/store (`op Wt,[Xn,#imm]`) into its register-offset form */
|
||||||
|
inline auto AsmSetLdStRegOffset = [](u32 ldstImm, u32 rm) -> u32 {
|
||||||
|
return (ldstImm & 0xC0C003FFu) | 0x38207800u | ((rm & 0x1Fu) << 16);
|
||||||
|
};
|
||||||
|
|
||||||
inline auto AsmIsLdpX = [](u32 ins) {
|
inline auto AsmIsLdpX = [](u32 ins) {
|
||||||
return (ins & 0xFE400000u) == 0xA8400000u;
|
return (ins & 0xFE400000u) == 0xA8400000u;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -173,6 +173,19 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
constexpr size_t DvfsTableEntryCount = 32;
|
constexpr size_t DvfsTableEntryCount = 32;
|
||||||
constexpr size_t DvfsTableEntryLimit = DvfsTableEntryCount - 1;
|
constexpr size_t DvfsTableEntryLimit = DvfsTableEntryCount - 1;
|
||||||
|
|
||||||
|
// EMC-only limit.
|
||||||
|
constexpr size_t EmcDvfsTableEntryCount = 64;
|
||||||
|
constexpr size_t EmcDvfsTableEntryLimit = EmcDvfsTableEntryCount - 1;
|
||||||
|
|
||||||
|
// The pcv SoC-voltage DVB table is a fixed 32-entry region. (it doesn't need to be larger)
|
||||||
|
constexpr size_t DvbTableCapacity = 32;
|
||||||
|
|
||||||
|
// extra .bss location, 0 until Patch() runs.
|
||||||
|
inline uintptr_t g_pcv_scratch = 0;
|
||||||
|
|
||||||
|
inline uintptr_t g_pcv_cave = 0;
|
||||||
|
inline size_t g_pcv_cave_size = 0;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
size_t GetDvfsTableEntryCount(T *table_head) {
|
size_t GetDvfsTableEntryCount(T *table_head) {
|
||||||
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
|
||||||
|
|||||||
@@ -1,165 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) Switch-OC-Suite
|
|
||||||
*
|
|
||||||
* Copyright (c) 2023 hanai3Bi
|
|
||||||
*
|
|
||||||
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../oc_common.hpp"
|
|
||||||
#include "pcv_common.hpp"
|
|
||||||
#include "pcv_asm.hpp"
|
|
||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::erista {
|
|
||||||
|
|
||||||
constexpr cvb_entry_t CpuCvbTableDefault[] = {
|
|
||||||
// CPU_PLL_CVB_TABLE_ODN
|
|
||||||
{ 204000, {721094}, { } },
|
|
||||||
{ 306000, {754040}, { } },
|
|
||||||
{ 408000, {786986}, { } },
|
|
||||||
{ 510000, {819932}, { } },
|
|
||||||
{ 612000, {852878}, { } },
|
|
||||||
{ 714000, {885824}, { } },
|
|
||||||
{ 816000, {918770}, { } },
|
|
||||||
{ 918000, {951716}, { } },
|
|
||||||
{ 1020000, {984662}, { -2875621, 358099, -8585} },
|
|
||||||
{ 1122000, {1017608}, { -52225, 104159, -2816} },
|
|
||||||
{ 1224000, {1050554}, { 1076868, 8356, -727} },
|
|
||||||
{ 1326000, {1083500}, { 2208191, -84659, 1240} },
|
|
||||||
{ 1428000, {1116446}, { 2519460, -105063, 1611} },
|
|
||||||
{ 1581000, {1130000}, { 2889664, -122173, 1834} },
|
|
||||||
{ 1683000, {1168000}, { 5100873, -279186, 4747} },
|
|
||||||
{ 1785000, {1227500}, { 5100873, -279186, 4747} },
|
|
||||||
{ },
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u32 CpuVoltOfficial = 1227;
|
|
||||||
constexpr u32 CpuVminOfficial = 825;
|
|
||||||
constexpr u32 CpuTune0Low = 0xFFEAD0FF;
|
|
||||||
|
|
||||||
constexpr u32 CpuVoltL4T = 1257'000;
|
|
||||||
|
|
||||||
static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 };
|
|
||||||
static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "Invalid cpuVoltDvfsPattern size");
|
|
||||||
|
|
||||||
static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 };
|
|
||||||
static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "Invalid cpuVoltageThermalPattern size");
|
|
||||||
|
|
||||||
constexpr u32 GpuClkPllLimit = 2'600'000;
|
|
||||||
constexpr u32 GpuClkPllMax = 921'600'000;
|
|
||||||
constexpr u32 GpuVminOfficial = 810;
|
|
||||||
|
|
||||||
constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 };
|
|
||||||
|
|
||||||
static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, };
|
|
||||||
static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern");
|
|
||||||
|
|
||||||
static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 };
|
|
||||||
static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "Invalid gpuVoltageThermalPattern size");
|
|
||||||
|
|
||||||
/* GPU Max Clock asm Pattern:
|
|
||||||
*
|
|
||||||
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
|
|
||||||
* sf | opc | | hw | imm16 | Rd
|
|
||||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
|
||||||
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
|
|
||||||
*
|
|
||||||
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
|
|
||||||
* sf | opc | | hw | imm16 | Rd
|
|
||||||
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
|
|
||||||
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
|
|
||||||
*/
|
|
||||||
inline constexpr u32 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 };
|
|
||||||
|
|
||||||
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
|
|
||||||
return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]);
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr cvb_entry_t GpuCvbTableDefault[] = {
|
|
||||||
// NA_FREQ_CVB_TABLE
|
|
||||||
{ 76800, {}, { 814294, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 153600, {}, { 856185, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 230400, {}, { 898077, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 307200, {}, { 939968, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 384000, {}, { 981860, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 460800, {}, { 1023751, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 537600, {}, { 1065642, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 614400, {}, { 1107534, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 691200, {}, { 1149425, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 768000, {}, { 1191317, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 844800, {}, { 1233208, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ 921600, {}, { 1275100, 8144, -940, 808, -21583, 226, } },
|
|
||||||
{ },
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u32 EmcListDefault[] = { 40800, 68000, 102000, 204000, 408000, 665600, 800000, 1065600, 1331200, 1600000, };
|
|
||||||
constexpr u32 EmcListSizeDefault = std::size(EmcListDefault);
|
|
||||||
constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1;
|
|
||||||
|
|
||||||
constexpr u32 MemVoltHOS = 1125'000;
|
|
||||||
constexpr u32 EmcClkPllmLimit = 1866'000'000;
|
|
||||||
|
|
||||||
constexpr u32 MTC_TABLE_REV = 7;
|
|
||||||
constexpr u32 MtcTableCountDefault = 10;
|
|
||||||
|
|
||||||
constexpr size_t MtcFullTableSize = sizeof(EristaMtcTable) * MtcTableCountDefault;
|
|
||||||
constexpr u32 MtcFullTableCount = 3;
|
|
||||||
|
|
||||||
/* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
|
|
||||||
enum DramId {
|
|
||||||
ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH = 0,
|
|
||||||
ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE = 1,
|
|
||||||
ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC = 2,
|
|
||||||
ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH = 4,
|
|
||||||
ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum MtcTableIndex {
|
|
||||||
T210SdevEmcDvfsTableS4gb01 = 0, /* HB-MGCH, WT:C */
|
|
||||||
T210SdevEmcDvfsTableS6gb01 = 1, /* HM-MGCH */
|
|
||||||
T210SdevEmcDvfsTableH4gb01 = 2, /* HR-NLE */
|
|
||||||
MtcTableIndex_Invalid = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MtcDramIndex {
|
|
||||||
DramId dramId;
|
|
||||||
MtcTableIndex index;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* TODO: Test 6gb and 8gb. */
|
|
||||||
const inline MtcDramIndex mtcIndexTable[] = {
|
|
||||||
{ ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, },
|
|
||||||
{ ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC, T210SdevEmcDvfsTableS4gb01, },
|
|
||||||
{ ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, },
|
|
||||||
{ ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX, T210SdevEmcDvfsTableS6gb01, },
|
|
||||||
{ ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, },
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr u32 MtcBrAsm = 0xD61F0140;
|
|
||||||
constexpr u32 MtcMovAsm = 0x52800148;
|
|
||||||
constexpr u32 MtcAdrpAsm = 0xD0000081;
|
|
||||||
constexpr u32 MtcBlIns = 0x97ffae64;
|
|
||||||
constexpr u32 MtcAddAsm = 0x91131821;
|
|
||||||
|
|
||||||
ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) {
|
|
||||||
/* This builds an address that gets returned, so the register must be x0 by convention. */
|
|
||||||
return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -37,6 +37,9 @@
|
|||||||
".text\n"); \
|
".text\n"); \
|
||||||
extern "C" __attribute__((visibility("hidden"))) type *name
|
extern "C" __attribute__((visibility("hidden"))) type *name
|
||||||
|
|
||||||
|
#define DECLARE_HOOK_PAYLOAD_PTR(type, name) \
|
||||||
|
extern "C" __attribute__((visibility("hidden"))) type *name
|
||||||
|
|
||||||
#define HOOK_PAYLOAD_PTR(type, name) \
|
#define HOOK_PAYLOAD_PTR(type, name) \
|
||||||
([]() -> type * { \
|
([]() -> type * { \
|
||||||
type **_hoc_pp; \
|
type **_hoc_pp; \
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# pull in common stratosphere sysmodule configuration
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||||
|
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
|
||||||
|
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk
|
||||||
|
|
||||||
|
ifneq ($(strip $(HOC_UART_LOG)),)
|
||||||
|
export CFLAGS += -DHOC_UART_LOG=$(HOC_UART_LOG)
|
||||||
|
export CXXFLAGS += -DHOC_UART_LOG=$(HOC_UART_LOG)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ATMOSPHERE_SYSTEM_MODULE_TARGETS := kip
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(__RECURSIVE__),1)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
|
||||||
|
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
|
||||||
|
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
|
||||||
|
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
||||||
|
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
$(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(ATMOSPHERE_BUILD_DIR)
|
||||||
|
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||||
|
|
||||||
|
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
|
||||||
|
|
||||||
|
ifeq ($(strip $(CONFIG_JSON)),)
|
||||||
|
jsons := $(wildcard *.json)
|
||||||
|
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring config.json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/config.json
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: clean all check_lib
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||||
|
@$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \
|
||||||
|
DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \
|
||||||
|
--no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \
|
||||||
|
-f $(THIS_MAKEFILE)
|
||||||
|
|
||||||
|
$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib
|
||||||
|
@$(SILENTCMD)echo "Checked library."
|
||||||
|
|
||||||
|
ifeq ($(ATMOSPHERE_CHECKED_LIBSTRATOSPHERE),1)
|
||||||
|
check_lib:
|
||||||
|
else
|
||||||
|
check_lib:
|
||||||
|
@$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
@rm -fr $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all : $(foreach target,$(ATMOSPHERE_SYSTEM_MODULE_TARGETS),$(OUTPUT).$(target))
|
||||||
|
|
||||||
|
$(OUTPUT).kip : $(OUTPUT).elf
|
||||||
|
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||||
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a
|
||||||
|
|
||||||
|
%.npdm : %.npdm.json
|
||||||
|
@echo built ... $< $@
|
||||||
|
@npdmtool $< $@
|
||||||
|
@echo built ... $(notdir $@)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -23,178 +24,110 @@
|
|||||||
#define ALIGN_PADDING 0x100000
|
#define ALIGN_PADDING 0x100000
|
||||||
#define CACHE_LINE_SIZE 128
|
#define CACHE_LINE_SIZE 128
|
||||||
|
|
||||||
#define BW_MAX_THREADS 4
|
struct f_data {
|
||||||
|
void (*func)(int64_t *, int64_t *, int);
|
||||||
|
int64_t *arg1;
|
||||||
|
int64_t *arg2;
|
||||||
|
int arg3;
|
||||||
|
};
|
||||||
|
|
||||||
typedef void (*bench_kernel_fn)(int64_t *dst, int64_t *src, long size);
|
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;
|
||||||
|
|
||||||
struct bw_pool;
|
static void *thread_func(void *data) {
|
||||||
|
struct f_data *d = data;
|
||||||
typedef struct {
|
pthread_mutex_lock(&p_lock);
|
||||||
Thread thread;
|
p_worker_not_ready--;
|
||||||
Semaphore start;
|
if (!p_worker_not_ready)
|
||||||
struct bw_pool *pool;
|
pthread_cond_signal(&p_ready);
|
||||||
bench_kernel_fn func;
|
while (p_workers_ready != 1)
|
||||||
int64_t *dst;
|
pthread_cond_wait(&p_start, &p_lock);
|
||||||
int64_t *src;
|
pthread_mutex_unlock(&p_lock);
|
||||||
long size;
|
(d->func)(d->arg1, d->arg2, d->arg3);
|
||||||
int core;
|
pthread_exit(NULL);
|
||||||
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 bool bw_pool_init(bw_pool_t *p, int threads) {
|
static void parallel_run(void) {
|
||||||
memset(p, 0, sizeof(*p));
|
pthread_mutex_lock(&p_lock);
|
||||||
if (threads > BW_MAX_THREADS)
|
p_workers_ready = 1;
|
||||||
threads = BW_MAX_THREADS;
|
pthread_mutex_unlock(&p_lock);
|
||||||
p->threads = threads;
|
pthread_cond_broadcast(&p_start);
|
||||||
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 bw_pool_free(bw_pool_t *p) {
|
static void parallel_init(int threads) {
|
||||||
for (int i = 0; i < p->threads; i++) {
|
pthread_attr_t attr;
|
||||||
bw_worker_t *w = &p->workers[i];
|
pthread_cond_init(&p_ready, NULL);
|
||||||
if (w->created) {
|
pthread_cond_init(&p_start, NULL);
|
||||||
w->func = NULL; /* shutdown */
|
pthread_mutex_init(&p_lock, NULL);
|
||||||
semaphoreSignal(&w->start);
|
p_worker_not_ready = threads;
|
||||||
}
|
p_workers_ready = 0;
|
||||||
}
|
pthread_attr_init(&attr);
|
||||||
for (int i = 0; i < p->threads; i++) {
|
if (!p_worker || !worker_data) {
|
||||||
bw_worker_t *w = &p->workers[i];
|
p_worker = malloc(threads * sizeof(pthread_t));
|
||||||
if (w->created) {
|
worker_data = malloc(threads * sizeof(struct f_data));
|
||||||
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++)
|
for (int i = 0; i < threads; i++)
|
||||||
semaphoreSignal(&p->workers[i].start);
|
pthread_create(p_worker + i, &attr, thread_func, worker_data + i);
|
||||||
for (int i = 0; i < threads; i++)
|
pthread_mutex_lock(&p_lock);
|
||||||
semaphoreWait(&p->done);
|
while (p_worker_not_ready != 0)
|
||||||
uint64_t sum = 0;
|
pthread_cond_wait(&p_ready, &p_lock);
|
||||||
for (int i = 0; i < threads; i++)
|
pthread_mutex_unlock(&p_lock);
|
||||||
sum += p->workers[i].ticks;
|
|
||||||
return (double)armTicksToNs(sum) / 1000000000.0 / threads;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BW_GPR_CLOBBERS \
|
static void aligned_block_copy(int64_t *__restrict dst_, int64_t *__restrict src, int size) {
|
||||||
"x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18"
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void aligned_block_copy(int64_t *dst, int64_t *src, long size) {
|
static void aligned_block_fetch(int64_t *__restrict dst, int64_t *__restrict src_, int size) {
|
||||||
__asm__ __volatile__(
|
volatile int64_t *src = src_;
|
||||||
"1:\n"
|
(void)dst;
|
||||||
"ldp x3, x4, [%[s], #0x00]\n"
|
while ((size -= 64) >= 0) {
|
||||||
"ldp x5, x6, [%[s], #0x10]\n"
|
*src++;
|
||||||
"ldp x7, x8, [%[s], #0x20]\n"
|
*src++;
|
||||||
"ldp x9, x10, [%[s], #0x30]\n"
|
*src++;
|
||||||
"ldp x11, x12, [%[s], #0x40]\n"
|
*src++;
|
||||||
"ldp x13, x14, [%[s], #0x50]\n"
|
*src++;
|
||||||
"ldp x15, x16, [%[s], #0x60]\n"
|
*src++;
|
||||||
"ldp x17, x18, [%[s], #0x70]\n"
|
*src++;
|
||||||
"add %[s], %[s], #0x80\n"
|
*src++;
|
||||||
"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_fetch(int64_t *dst, int64_t *src, long size) {
|
|
||||||
(void)src;
|
static void aligned_block_fill(int64_t *__restrict dst_, int64_t *__restrict src, int size) {
|
||||||
__asm__ __volatile__(
|
volatile int64_t *dst = dst_;
|
||||||
"1:\n"
|
int64_t data = *src;
|
||||||
"ldp x3, x4, [%[p], #0x00]\n"
|
while ((size -= 64) >= 0) {
|
||||||
"ldp x5, x6, [%[p], #0x10]\n"
|
*dst++ = data;
|
||||||
"ldp x7, x8, [%[p], #0x20]\n"
|
*dst++ = data;
|
||||||
"ldp x9, x10, [%[p], #0x30]\n"
|
*dst++ = data;
|
||||||
"ldp x11, x12, [%[p], #0x40]\n"
|
*dst++ = data;
|
||||||
"ldp x13, x14, [%[p], #0x50]\n"
|
*dst++ = data;
|
||||||
"ldp x15, x16, [%[p], #0x60]\n"
|
*dst++ = data;
|
||||||
"ldp x17, x18, [%[p], #0x70]\n"
|
*dst++ = data;
|
||||||
"add %[p], %[p], #0x80\n"
|
*dst++ = data;
|
||||||
"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) {
|
static double gettime(void) {
|
||||||
@@ -203,29 +136,47 @@ static double gettime(void) {
|
|||||||
return (double)((int64_t)tv.tv_sec * 1000000 + tv.tv_usec) / 1000000.;
|
return (double)((int64_t)tv.tv_sec * 1000000 + tv.tv_usec) / 1000000.;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double bandwidth_bench_helper(bw_pool_t *pool, int threads, int64_t *dstbuf, int64_t *srcbuf, long size, bench_kernel_fn f) {
|
static double bandwidth_bench_helper(int threads, int64_t *dstbuf, int64_t *srcbuf, int size, void (*f)(int64_t *, int64_t *, int)) {
|
||||||
int i, loopcount, innerloopcount, n;
|
int i, loopcount, innerloopcount, n;
|
||||||
double t, speed, maxspeed;
|
double t, t1, t2, speed, maxspeed, s, s0, s1, s2;
|
||||||
|
|
||||||
if (!pool || !pool->ok)
|
|
||||||
return 0.;
|
|
||||||
|
|
||||||
|
s = s0 = s1 = s2 = 0.;
|
||||||
maxspeed = 0.;
|
maxspeed = 0.;
|
||||||
for (n = 0; n < MAXREPEATS; n++) {
|
for (n = 0; n < MAXREPEATS; n++) {
|
||||||
bw_run_once(pool, f, dstbuf, srcbuf, size);
|
|
||||||
loopcount = 0;
|
loopcount = 0;
|
||||||
innerloopcount = 1;
|
innerloopcount = 1;
|
||||||
t = 0.;
|
t = 0.;
|
||||||
do {
|
do {
|
||||||
loopcount += innerloopcount;
|
loopcount += innerloopcount;
|
||||||
for (i = 0; i < innerloopcount; i++)
|
for (i = 0; i < innerloopcount; i++) {
|
||||||
t += bw_run_once(pool, f, dstbuf, srcbuf, size);
|
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;
|
||||||
|
}
|
||||||
innerloopcount *= 2;
|
innerloopcount *= 2;
|
||||||
} while (t < 0.25);
|
} while (t < 0.5);
|
||||||
|
|
||||||
speed = (double)size * threads * loopcount / t / 1000000.;
|
speed = (double)size * threads * loopcount / t / 1000000.;
|
||||||
|
s0 += 1.;
|
||||||
|
s1 += speed;
|
||||||
|
s2 += speed * speed;
|
||||||
if (speed > maxspeed)
|
if (speed > maxspeed)
|
||||||
maxspeed = speed;
|
maxspeed = speed;
|
||||||
|
if (s0 > 2.) {
|
||||||
|
s = sqrt((s0 * s2 - s1 * s1) / (s0 * (s0 - 1)));
|
||||||
|
if (s < maxspeed / 1000.)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return maxspeed;
|
return maxspeed;
|
||||||
}
|
}
|
||||||
@@ -355,16 +306,13 @@ void bench_run_full(bench_results_t *out, bench_progress_fn progress, void *user
|
|||||||
|
|
||||||
int64_t *srcbuf, *dstbuf;
|
int64_t *srcbuf, *dstbuf;
|
||||||
void *poolbuf = alloc_nonaliased_buffers((void **)&srcbuf, size * threads, (void **)&dstbuf, size * threads, NULL, 0);
|
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);
|
STEP("CPU copy", 0.40f);
|
||||||
out->cpu_copy = bandwidth_bench_helper(&bw, threads, dstbuf, srcbuf, size, aligned_block_copy);
|
out->cpu_copy = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_copy);
|
||||||
STEP("CPU read", 0.55f);
|
STEP("CPU read", 0.55f);
|
||||||
out->cpu_read = bandwidth_bench_helper(&bw, threads, dstbuf, srcbuf, size, aligned_block_fetch);
|
out->cpu_read = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_fetch);
|
||||||
STEP("CPU write", 0.70f);
|
STEP("CPU write", 0.70f);
|
||||||
out->cpu_write = bandwidth_bench_helper(&bw, threads, dstbuf, srcbuf, size, aligned_block_fill);
|
out->cpu_write = bandwidth_bench_helper(threads, dstbuf, srcbuf, size, aligned_block_fill);
|
||||||
bw_pool_free(&bw);
|
|
||||||
free(poolbuf);
|
free(poolbuf);
|
||||||
|
|
||||||
STEP("Latency", 0.85f);
|
STEP("Latency", 0.85f);
|
||||||
@@ -377,10 +325,9 @@ void bench_run_full(bench_results_t *out, bench_progress_fn progress, void *user
|
|||||||
struct bench_ctx {
|
struct bench_ctx {
|
||||||
int phase;
|
int phase;
|
||||||
bool is_4gb;
|
bool is_4gb;
|
||||||
void *buf;
|
void *pool;
|
||||||
int64_t *src;
|
int64_t *src;
|
||||||
int64_t *dst;
|
int64_t *dst;
|
||||||
bw_pool_t bw;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bench_ctx *bench_begin(void) {
|
bench_ctx *bench_begin(void) {
|
||||||
@@ -388,8 +335,7 @@ bench_ctx *bench_begin(void) {
|
|||||||
if (!c)
|
if (!c)
|
||||||
return NULL;
|
return NULL;
|
||||||
c->is_4gb = (appletGetAppletType() == AppletType_Application);
|
c->is_4gb = (appletGetAppletType() == AppletType_Application);
|
||||||
c->buf = alloc_nonaliased_buffers((void **)&c->src, SIZE * 3, (void **)&c->dst, SIZE * 3, NULL, 0);
|
c->pool = alloc_nonaliased_buffers((void **)&c->src, SIZE * 3, (void **)&c->dst, SIZE * 3, NULL, 0);
|
||||||
bw_pool_init(&c->bw, 3);
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,25 +349,24 @@ bool bench_step(bench_ctx *c, bench_results_t *out, const char **label, float *f
|
|||||||
*frac = 0.25f;
|
*frac = 0.25f;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
out->cpu_copy = bandwidth_bench_helper(&c->bw, threads, c->dst, c->src, size, aligned_block_copy);
|
out->cpu_copy = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_copy);
|
||||||
*label = "CPU copy";
|
*label = "CPU copy";
|
||||||
*frac = 0.45f;
|
*frac = 0.45f;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
out->cpu_read = bandwidth_bench_helper(&c->bw, threads, c->dst, c->src, size, aligned_block_fetch);
|
out->cpu_read = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_fetch);
|
||||||
*label = "CPU read";
|
*label = "CPU read";
|
||||||
*frac = 0.60f;
|
*frac = 0.60f;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
out->cpu_write = bandwidth_bench_helper(&c->bw, threads, c->dst, c->src, size, aligned_block_fill);
|
out->cpu_write = bandwidth_bench_helper(threads, c->dst, c->src, size, aligned_block_fill);
|
||||||
*label = "CPU write";
|
*label = "CPU write";
|
||||||
*frac = 0.75f;
|
*frac = 0.75f;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
bw_pool_free(&c->bw);
|
if (c->pool) {
|
||||||
if (c->buf) {
|
free(c->pool);
|
||||||
free(c->buf);
|
c->pool = NULL;
|
||||||
c->buf = NULL;
|
|
||||||
}
|
}
|
||||||
latency_bench(&out->l2_ns, &out->ram_ns);
|
latency_bench(&out->l2_ns, &out->ram_ns);
|
||||||
*label = "Latency";
|
*label = "Latency";
|
||||||
@@ -439,8 +384,7 @@ bool bench_step(bench_ctx *c, bench_results_t *out, const char **label, float *f
|
|||||||
void bench_end(bench_ctx *c) {
|
void bench_end(bench_ctx *c) {
|
||||||
if (!c)
|
if (!c)
|
||||||
return;
|
return;
|
||||||
bw_pool_free(&c->bw);
|
if (c->pool)
|
||||||
if (c->buf)
|
free(c->pool);
|
||||||
free(c->buf);
|
|
||||||
free(c);
|
free(c);
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,960 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "ldr_capabilities.hpp"
|
|
||||||
#include "ldr_content_management.hpp"
|
|
||||||
#include "ldr_development_manager.hpp"
|
|
||||||
#include "ldr_launch_record.hpp"
|
|
||||||
#include "ldr_meta.hpp"
|
|
||||||
#include "ldr_patcher.hpp"
|
|
||||||
#include "ldr_process_creation.hpp"
|
|
||||||
#include "ldr_ro_manager.hpp"
|
|
||||||
#include "oc/oc_loader.hpp"
|
|
||||||
|
|
||||||
namespace ams::ldr {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/* Convenience defines. */
|
|
||||||
constexpr size_t SystemResourceSizeMax = 0x1FE00000;
|
|
||||||
constexpr size_t AutoLoadModuleSizeMax = 0x800000000;
|
|
||||||
|
|
||||||
/* Types. */
|
|
||||||
enum NsoIndex {
|
|
||||||
Nso_Rtld = 0,
|
|
||||||
Nso_Main = 1,
|
|
||||||
Nso_Wkc0 = 2,
|
|
||||||
Nso_Wkc1 = 3,
|
|
||||||
Nso_Wkc2 = 4,
|
|
||||||
Nso_Wkc3 = 5,
|
|
||||||
Nso_Wkc4 = 6,
|
|
||||||
Nso_Wkc5 = 7,
|
|
||||||
Nso_Wkc6 = 8,
|
|
||||||
Nso_Wkc7 = 9,
|
|
||||||
Nso_Wkc8 = 10,
|
|
||||||
Nso_Wkc9 = 11,
|
|
||||||
Nso_SubSdk0 = 12,
|
|
||||||
Nso_SubSdk1 = 13,
|
|
||||||
Nso_SubSdk2 = 14,
|
|
||||||
Nso_SubSdk3 = 15,
|
|
||||||
Nso_SubSdk4 = 16,
|
|
||||||
Nso_SubSdk5 = 17,
|
|
||||||
Nso_SubSdk6 = 18,
|
|
||||||
Nso_SubSdk7 = 19,
|
|
||||||
Nso_SubSdk8 = 20,
|
|
||||||
Nso_SubSdk9 = 21,
|
|
||||||
Nso_Sdk = 22,
|
|
||||||
Nso_Count,
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr inline const char *NsoPaths[Nso_Count] = {
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/rtld"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/main"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc0"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc1"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc2"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc3"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc4"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc5"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc6"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc7"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc8"),
|
|
||||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc9"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk0"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk1"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk2"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk3"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk4"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk5"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk6"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk7"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk8"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk9"),
|
|
||||||
ENCODE_ATMOSPHERE_CODE_PATH("/sdk"),
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr const char *GetNsoPath(size_t idx) {
|
|
||||||
AMS_ABORT_UNLESS(idx < Nso_Count);
|
|
||||||
return NsoPaths[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ProcessInfo {
|
|
||||||
os::NativeHandle process_handle;
|
|
||||||
uintptr_t code_address;
|
|
||||||
size_t total_size;
|
|
||||||
uintptr_t args_address;
|
|
||||||
size_t args_size;
|
|
||||||
uintptr_t nso_address[Nso_Count];
|
|
||||||
size_t nso_size[Nso_Count];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AutoLoadModuleInfo {
|
|
||||||
bool has_rtld;
|
|
||||||
bool has_main;
|
|
||||||
bool has_sdk;
|
|
||||||
bool has_subsdk;
|
|
||||||
s8 nso_indices[Nso_Count];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AutoLoadModuleContext {
|
|
||||||
NsoHeader *headers;
|
|
||||||
int nso_count;
|
|
||||||
int rtld_idx;
|
|
||||||
int main_nso_idx;
|
|
||||||
int sdk_nso_idx;
|
|
||||||
AutoLoadModuleInfo ali;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Global NSO header cache. */
|
|
||||||
NsoHeader g_nso_headers[Nso_Count];
|
|
||||||
|
|
||||||
/* Pcv/Ptm/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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Executable → Regular
File mode changed.
@@ -82,9 +82,10 @@ typedef struct {
|
|||||||
u8 custRev;
|
u8 custRev;
|
||||||
u16 kipVersion;
|
u16 kipVersion;
|
||||||
bool isKipLoaded;
|
bool isKipLoaded;
|
||||||
|
bool rebootRequired;
|
||||||
|
|
||||||
// Reserved for future use
|
// Reserved for future use
|
||||||
u8 reserved[0x35A];
|
u8 reserved[0x359];
|
||||||
} HocClkContext;
|
} HocClkContext;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -95,7 +96,7 @@ typedef struct
|
|||||||
};
|
};
|
||||||
} HocClkTitleProfileList;
|
} HocClkTitleProfileList;
|
||||||
|
|
||||||
#define HOCCLK_FREQ_LIST_MAX 48
|
#define HOCCLK_FREQ_LIST_MAX 64
|
||||||
|
|
||||||
#define HOCCLK_GLOBAL_PROFILE_TID 0xA111111111111111
|
#define HOCCLK_GLOBAL_PROFILE_TID 0xA111111111111111
|
||||||
|
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
|
|||||||
case KipConfigValue_low_t7_tWTR:
|
case KipConfigValue_low_t7_tWTR:
|
||||||
return pretty ? "Low T7 - tWTR" : "low_t7_tWTR";
|
return pretty ? "Low T7 - tWTR" : "low_t7_tWTR";
|
||||||
case KipConfigValue_low_t8_tREFI:
|
case KipConfigValue_low_t8_tREFI:
|
||||||
return pretty ? "Low T8 - tREFI" : "low_t8_tREFI";
|
return pretty ? "Low T8 - tREFI" : "low_t7_tREFI";
|
||||||
|
|
||||||
case KipConfigValue_read_latency_1333:
|
case KipConfigValue_read_latency_1333:
|
||||||
return pretty ? "1333 Read Latency" : "read_latency_1333";
|
return pretty ? "1333 Read Latency" : "read_latency_1333";
|
||||||
@@ -572,7 +572,6 @@ static inline uint64_t hocclkDefaultConfigValue(HocClkConfigValue val)
|
|||||||
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
|
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
|
||||||
case HocClkConfigValue_MarikoMiddleFreqs:
|
case HocClkConfigValue_MarikoMiddleFreqs:
|
||||||
case HocClkConfigValue_LiveGpuVoltage:
|
case HocClkConfigValue_LiveGpuVoltage:
|
||||||
case HocClkConfigValue_AutoRAMCPUOverclock:
|
|
||||||
return 0ULL;
|
return 0ULL;
|
||||||
case HocClkConfigValue_RamDisplayUnit:
|
case HocClkConfigValue_RamDisplayUnit:
|
||||||
return (uint64_t)RamDisplayUnit_MHz;
|
return (uint64_t)RamDisplayUnit_MHz;
|
||||||
@@ -585,6 +584,7 @@ static inline uint64_t hocclkDefaultConfigValue(HocClkConfigValue val)
|
|||||||
case HocClkConfigValue_ThermalThrottle:
|
case HocClkConfigValue_ThermalThrottle:
|
||||||
case HocClkConfigValue_IsFirstLoad:
|
case HocClkConfigValue_IsFirstLoad:
|
||||||
case HocClkConfigValue_DVFSMode:
|
case HocClkConfigValue_DVFSMode:
|
||||||
|
case HocClkConfigValue_AutoRAMCPUOverclock:
|
||||||
return 1ULL;
|
return 1ULL;
|
||||||
case HocClkConfigValue_AutoRamCpuCpuOCFreq:
|
case HocClkConfigValue_AutoRamCpuCpuOCFreq:
|
||||||
return 1683000ULL;
|
return 1683000ULL;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk
|
|||||||
# version control constants
|
# version control constants
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
|
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
|
||||||
APP_VERSION := 2.6.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this
|
APP_VERSION := 3.0.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this
|
||||||
TARGET_VERSION := $(APP_VERSION)
|
TARGET_VERSION := $(APP_VERSION)
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -96,6 +96,15 @@ class AppOverlay : public tsl::Overlay {
|
|||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HocClkContext context = {};
|
||||||
|
if (R_SUCCEEDED(hocclkIpcGetCurrentContext(&context)) && context.rebootRequired) {
|
||||||
|
return initially<FatalGui>("Horizon OC has been updated.\n\n"
|
||||||
|
"\n"
|
||||||
|
"Please reboot your console\n\n"
|
||||||
|
"to finish applying the update.",
|
||||||
|
"");
|
||||||
|
}
|
||||||
|
|
||||||
return initially<MainGui>();
|
return initially<MainGui>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ void BaseGui::preDraw(tsl::gfx::Renderer *renderer) {
|
|||||||
|
|
||||||
drawDynamicUltraText(renderer, LOGO_TEXT_X, TEXT_Y, LOGO_LABEL_FONT_SIZE, STATIC_TEAL, false);
|
drawDynamicUltraText(renderer, LOGO_TEXT_X, TEXT_Y, LOGO_LABEL_FONT_SIZE, STATIC_TEAL, false);
|
||||||
|
|
||||||
static const std::string versionStr = "Version " + getVersionString() + " \"Gaea\"";
|
static const std::string versionStr = "Version " + getVersionString() + " \"Athena\"";
|
||||||
static constexpr tsl::Color versionColor(9, 9, 9, 15);
|
static constexpr tsl::Color versionColor(9, 9, 9, 15);
|
||||||
static constexpr s32 vx = LOGO_TEXT_X + 15;
|
static constexpr s32 vx = LOGO_TEXT_X + 15;
|
||||||
static constexpr s32 vy = TEXT_Y + 18;
|
static constexpr s32 vy = TEXT_Y + 18;
|
||||||
|
|||||||
@@ -228,15 +228,15 @@ std::vector<std::string> ConfigInfoStrings(HocClkConfigValue val, bool isMariko,
|
|||||||
case KipConfigValue_stepMode:
|
case KipConfigValue_stepMode:
|
||||||
return {
|
return {
|
||||||
"The step that RAM clocks take.",
|
"The step that RAM clocks take.",
|
||||||
"Options (with examples):",
|
"Options:",
|
||||||
|
" - 33MHz - 33 MHz step (ex. 1600, 1633, 1666, 1700, etc.)",
|
||||||
" - 66MHz - 66 MHz step (ex. 1600, 1666, 1733, etc.)",
|
" - 66MHz - 66 MHz step (ex. 1600, 1666, 1733, etc.)",
|
||||||
" - 100MHz - 100 MHz step (ex. 1600, 1700, 1800, etc.)",
|
" - 100MHz - 100 MHz step (ex. 1600, 1700, 1800, etc.)",
|
||||||
" - 133MHz - 66 MHz step (ex. 1600, 1733, 1866, etc.)",
|
" - 133MHz - 133 MHz step (ex. 1600, 1733, 1866, etc.)",
|
||||||
" - JEDEC:",
|
" - JEDEC:",
|
||||||
" - 1600, 1866, 1996, 2133, 2400, 2666, 2933 and 3200 MHz are used",
|
" - 1600, 1866, 1996, 2133, 2400, 2666, 2933 and 3200 MHz are used",
|
||||||
"The RAM max clock will always be available regardless of the step mode, but the intermediate frequencies will be limited by the selected step mode.",
|
"The RAM max clock will always be available regardless of the step mode, but the intermediate frequencies will be limited by the selected step mode.",
|
||||||
"This setting does not affect performance and the option you choose mostly is based on your personal taste",
|
"This setting does not affect performance and the option you choose mostly is based on your personal taste",
|
||||||
"33 MHz step mode is not possible due to certain limitations of Horizon OS",
|
|
||||||
"Default: 66 MHz",
|
"Default: 66 MHz",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,12 @@
|
|||||||
|
|
||||||
std::map<uint32_t, std::string> cpu_freq_label_m = {
|
std::map<uint32_t, std::string> cpu_freq_label_m = {
|
||||||
{ 612000000, "Sleep Mode" }, { 1020000000, "Stock" }, { 1224000000, "Dev OC" }, { 1785000000, "Boost Mode" },
|
{ 612000000, "Sleep Mode" }, { 1020000000, "Stock" }, { 1224000000, "Dev OC" }, { 1785000000, "Boost Mode" },
|
||||||
{ 1963000000, "Safe Max" }, { 2397000000, "Unsafe Max" }, { 2805000000, "Absolute Max" },
|
{ 1963000000, "Safe Max" }, { 2397000000, "Unsafe Max" }, { 2703000000, "Absolute Max" },
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<uint32_t, std::string> cpu_freq_label_m_uv = {
|
std::map<uint32_t, std::string> cpu_freq_label_m_uv = {
|
||||||
{ 612000000, "Sleep Mode" }, { 1020000000, "Stock" }, { 1224000000, "Dev OC" }, { 1785000000, "Boost Mode" },
|
{ 612000000, "Sleep Mode" }, { 1020000000, "Stock" }, { 1224000000, "Dev OC" }, { 1785000000, "Boost Mode" },
|
||||||
{ 2397000000, "Safe Max" }, { 2499000000, "Unsafe Max" }, { 2805000000, "Absolute Max" },
|
{ 2397000000, "Safe Max" }, { 2499000000, "Unsafe Max" }, { 2703000000, "Absolute Max" },
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<uint32_t, std::string> cpu_freq_label_e = {
|
std::map<uint32_t, std::string> cpu_freq_label_e = {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "main_gui.h"
|
#include "main_gui.h"
|
||||||
#include "misc_gui.h"
|
#include "misc_gui.h"
|
||||||
#include "ult_ext.h"
|
#include "ult_ext.h"
|
||||||
|
#include "update_gui.h"
|
||||||
|
|
||||||
tsl::elm::Element *MainGui::baseUI() {
|
tsl::elm::Element *MainGui::baseUI() {
|
||||||
auto *list = new BoxClippedList();
|
auto *list = new BoxClippedList();
|
||||||
@@ -113,6 +114,17 @@ void MainGui::listUI() {
|
|||||||
});
|
});
|
||||||
this->listElement->addItem(aboutItem);
|
this->listElement->addItem(aboutItem);
|
||||||
|
|
||||||
|
tsl::elm::ListItem *updateItem = new tsl::elm::ListItem("Updates");
|
||||||
|
updateItem->setClickListener([this](u64 keys) {
|
||||||
|
if ((keys & HidNpadButton_A) == HidNpadButton_A && this->context) {
|
||||||
|
tsl::swapTo<UpdateGui>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
this->listElement->addItem(updateItem);
|
||||||
|
|
||||||
if (!lastItemName.empty()) {
|
if (!lastItemName.empty()) {
|
||||||
this->listElement->jumpToItem(lastItemName);
|
this->listElement->jumpToItem(lastItemName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -925,78 +925,6 @@ class ExperimentalSettingsSubMenuGui : public MiscGui {
|
|||||||
addConfigButton(HocClkConfigValue_AulaDisplayColorPreset, "Display Color Preset", ValueRange(0, 1, 1, "", 0), "Display Color Preset",
|
addConfigButton(HocClkConfigValue_AulaDisplayColorPreset, "Display Color Preset", ValueRange(0, 1, 1, "", 0), "Display Color Preset",
|
||||||
&thresholdsDisabled, {}, displayClrPreset, false, false);
|
&thresholdsDisabled, {}, displayClrPreset, false, false);
|
||||||
}
|
}
|
||||||
std::vector<NamedValue> clkOptionsRamOc = {
|
|
||||||
NamedValue("1122 MHz", 1122000), NamedValue("1224 MHz", 1224000), NamedValue("1326 MHz", 1326000), NamedValue("1428 MHz", 1428000),
|
|
||||||
NamedValue("1581 MHz", 1581000), NamedValue("1683 MHz", 1683000, "Default"), NamedValue("1785 MHz", 1785000), NamedValue("1887 MHz", 1887000),
|
|
||||||
NamedValue("1963 MHz", 1963500), NamedValue("2091 MHz", 2091000), NamedValue("2193 MHz", 2193000), NamedValue("2295 MHz", 2295000),
|
|
||||||
NamedValue("2397 MHz", 2397000), NamedValue("2499 MHz", 2499000), NamedValue("2601 MHz", 2601000), NamedValue("2703 MHz", 2703000),
|
|
||||||
NamedValue("2805 MHz", 2805000),
|
|
||||||
};
|
|
||||||
std::vector<NamedValue> emcMaxClock = {
|
|
||||||
NamedValue("1600 MHz", 1600000),
|
|
||||||
NamedValue("1633 MHz", 1633000),
|
|
||||||
NamedValue("1666 MHz", 1666000),
|
|
||||||
NamedValue("1700 MHz", 1700000),
|
|
||||||
NamedValue("1733 MHz", 1733000),
|
|
||||||
NamedValue("1766 MHz", 1766000),
|
|
||||||
NamedValue("1800 MHz", 1800000),
|
|
||||||
NamedValue("1833 MHz", 1833000),
|
|
||||||
NamedValue("1866 MHz", 1866000, "JEDEC."),
|
|
||||||
NamedValue("1900 MHz", 1900000),
|
|
||||||
NamedValue("1933 MHz", 1933000),
|
|
||||||
NamedValue("1966 MHz", 1966000),
|
|
||||||
NamedValue("1996 MHz", 1996800, "JEDEC."),
|
|
||||||
NamedValue("2000 MHz", 2000000),
|
|
||||||
NamedValue("2033 MHz", 2033000),
|
|
||||||
NamedValue("2066 MHz", 2066000),
|
|
||||||
NamedValue("2100 MHz", 2100000),
|
|
||||||
NamedValue("2133 MHz", 2133000, "JEDEC."),
|
|
||||||
NamedValue("2166 MHz", 2166000),
|
|
||||||
NamedValue("2200 MHz", 2200000),
|
|
||||||
NamedValue("2233 MHz", 2233000),
|
|
||||||
NamedValue("2266 MHz", 2266000),
|
|
||||||
NamedValue("2300 MHz", 2300000),
|
|
||||||
NamedValue("2333 MHz", 2333000),
|
|
||||||
NamedValue("2366 MHz", 2366000),
|
|
||||||
NamedValue("2400 MHz", 2400000, "JEDEC."),
|
|
||||||
NamedValue("2433 MHz", 2433000),
|
|
||||||
NamedValue("2466 MHz", 2466000),
|
|
||||||
NamedValue("2500 MHz", 2500000),
|
|
||||||
NamedValue("2533 MHz", 2533000),
|
|
||||||
NamedValue("2566 MHz", 2566000),
|
|
||||||
NamedValue("2600 MHz", 2600000),
|
|
||||||
NamedValue("2633 MHz", 2633000),
|
|
||||||
NamedValue("2666 MHz", 2666000, "JEDEC."),
|
|
||||||
NamedValue("2700 MHz", 2700000),
|
|
||||||
NamedValue("2733 MHz", 2733000),
|
|
||||||
NamedValue("2766 MHz", 2766000),
|
|
||||||
NamedValue("2800 MHz", 2800000),
|
|
||||||
NamedValue("2833 MHz", 2833000),
|
|
||||||
NamedValue("2866 MHz", 2866000),
|
|
||||||
NamedValue("2900 MHz", 2900000),
|
|
||||||
NamedValue("2933 MHz", 2933000, "JEDEC."),
|
|
||||||
NamedValue("2966 MHz", 2966000),
|
|
||||||
NamedValue("3000 MHz", 3000000),
|
|
||||||
NamedValue("3033 MHz", 3033000),
|
|
||||||
NamedValue("3066 MHz", 3066000),
|
|
||||||
NamedValue("3100 MHz", 3100000),
|
|
||||||
NamedValue("3133 MHz", 3133000),
|
|
||||||
NamedValue("3166 MHz", 3166000),
|
|
||||||
NamedValue("3200 MHz", 3200000, "JEDEC."),
|
|
||||||
NamedValue("3233 MHz", 3233000, "±1720 speedo"),
|
|
||||||
NamedValue("3266 MHz", 3266000, "±1740 speedo"),
|
|
||||||
NamedValue("3300 MHz", 3300000, "±1760 speedo"),
|
|
||||||
NamedValue("3333 MHz", 3333000, "±1780 speedo"),
|
|
||||||
NamedValue("3366 MHz", 3366000, "±1800 speedo"),
|
|
||||||
NamedValue("3400 MHz", 3400000, "±1820 speedo")
|
|
||||||
};
|
|
||||||
|
|
||||||
addConfigToggle(HocClkConfigValue_AutoRAMCPUOverclock, "Auto CPU RAM OC");
|
|
||||||
addConfigButton(HocClkConfigValue_AutoRamCpuCpuOCFreq, "Auto CPU RAM OC CPU clock", ValueRange(0, 0, 1, "", 1), "CPU Clock",
|
|
||||||
&thresholdsDisabled, {}, clkOptionsRamOc, false, false);
|
|
||||||
addConfigButton(HocClkConfigValue_AutoRamCpuRamOCThreshold, "Auto CPU RAM OC Threshold", ValueRange(0, 0, 1, "", 1), "RAM Clock",
|
|
||||||
&thresholdsDisabled, {}, emcMaxClock, false, false);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1204,7 +1132,7 @@ class RamSubmenuGui : public MiscGui {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ValueThresholds vdd2Thresholds(IsMariko() ? 1212500 : 1237500, IsMariko() ? 1250000 : 1275000);
|
ValueThresholds vdd2Thresholds(IsMariko() ? 1212500 : 1237500, IsMariko() ? 1250000 : 1275000);
|
||||||
addConfigButton(KipConfigValue_commonEmcMemVolt, "RAM VDD2 Voltage", ValueRange(912500, IsMariko() ? 1275000 : 1350000, 12500, "mV", 1000, 1), "Voltage",
|
addConfigButton(KipConfigValue_commonEmcMemVolt, "RAM VDD2 Voltage", ValueRange(912500, 1350000, 12500, "mV", 1000, 1), "Voltage",
|
||||||
&vdd2Thresholds, emc_voltage_label, noNamedValues, false, true);
|
&vdd2Thresholds, emc_voltage_label, noNamedValues, false, true);
|
||||||
|
|
||||||
if (IsMariko()) {
|
if (IsMariko()) {
|
||||||
@@ -1215,6 +1143,7 @@ class RamSubmenuGui : public MiscGui {
|
|||||||
|
|
||||||
if (IsMariko()) {
|
if (IsMariko()) {
|
||||||
std::vector<NamedValue> stepMode = {
|
std::vector<NamedValue> stepMode = {
|
||||||
|
NamedValue("33MHz", 4),
|
||||||
NamedValue("66MHz", 0),
|
NamedValue("66MHz", 0),
|
||||||
NamedValue("100MHz", 1),
|
NamedValue("100MHz", 1),
|
||||||
NamedValue("133MHz", 3), // Mantain compatability
|
NamedValue("133MHz", 3), // Mantain compatability
|
||||||
@@ -2190,19 +2119,25 @@ class CpuSubmenuGui : public MiscGui {
|
|||||||
{}, false, true);
|
{}, false, true);
|
||||||
|
|
||||||
ValueThresholds mCpuVoltThresholds(1160, 1180);
|
ValueThresholds mCpuVoltThresholds(1160, 1180);
|
||||||
addConfigButton(KipConfigValue_marikoCpuMaxVolt, "CPU Max Voltage", ValueRange(1000, 1235, 5, "mV", 1), "CPU Max Voltage",
|
addConfigButton(KipConfigValue_marikoCpuMaxVolt, "CPU Max Voltage", ValueRange(1000, 1200, 5, "mV", 1), "CPU Max Voltage",
|
||||||
&mCpuVoltThresholds, {}, {}, false, true);
|
&mCpuVoltThresholds, {}, {}, false, true);
|
||||||
|
|
||||||
std::vector<NamedValue> maxClkOptions = {
|
std::vector<NamedValue> maxClkOptions = {
|
||||||
NamedValue("1963 MHz", 1963500, "Rating"), NamedValue("2091 MHz", 2091000), NamedValue("2193 MHz", 2193000), NamedValue("2295 MHz", 2295000),
|
NamedValue("1963 MHz", 1963500, "Rating"), NamedValue("2091 MHz", 2091000), NamedValue("2193 MHz", 2193000), NamedValue("2295 MHz", 2295000),
|
||||||
NamedValue("2397 MHz", 2397000, "Safe Max"), NamedValue("2499 MHz", 2499000, "Unsafe Max"), NamedValue("2601 MHz", 2601000), NamedValue("2703 MHz", 2703000),
|
NamedValue("2397 MHz", 2397000, "Safe Max"), NamedValue("2499 MHz", 2499000, "Unsafe Max"), NamedValue("2601 MHz", 2601000), NamedValue("2703 MHz", 2703000, "Absolute Max"),
|
||||||
NamedValue("2805 MHz", 2805000, "Absolute Max"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
addConfigButton(KipConfigValue_marikoCpuMaxClock, "CPU Max Clock", ValueRange(0, 0, 1, "", 1), "CPU Max Clock",
|
addConfigButton(KipConfigValue_marikoCpuMaxClock, "CPU Max Clock", ValueRange(0, 0, 1, "", 1), "CPU Max Clock",
|
||||||
this->configList->values[KipConfigValue_marikoCpuUVHigh] ? &mCpuClockThresholdsUV : &mCpuClockThresholds, {},
|
this->configList->values[KipConfigValue_marikoCpuUVHigh] ? &mCpuClockThresholdsUV : &mCpuClockThresholds, {},
|
||||||
maxClkOptions, false, true);
|
maxClkOptions, false, true);
|
||||||
|
|
||||||
|
std::vector<NamedValue> clkOptionsRamOc = {
|
||||||
|
NamedValue("1122 MHz", 1122000), NamedValue("1224 MHz", 1224000), NamedValue("1326 MHz", 1326000), NamedValue("1428 MHz", 1428000),
|
||||||
|
NamedValue("1581 MHz", 1581000), NamedValue("1683 MHz", 1683000, "Default"), NamedValue("1785 MHz", 1785000), NamedValue("1887 MHz", 1887000),
|
||||||
|
NamedValue("1963 MHz", 1963500), NamedValue("2091 MHz", 2091000), NamedValue("2193 MHz", 2193000), NamedValue("2295 MHz", 2295000),
|
||||||
|
NamedValue("2397 MHz", 2397000), NamedValue("2499 MHz", 2499000), NamedValue("2601 MHz", 2601000), NamedValue("2703 MHz", 2703000),
|
||||||
|
};
|
||||||
|
|
||||||
addConfigButton(KipConfigValue_marikoCpuBoostClock, "CPU Boost Clock", ValueRange(0, 0, 1, "", 1), "CPU Boost Clock",
|
addConfigButton(KipConfigValue_marikoCpuBoostClock, "CPU Boost Clock", ValueRange(0, 0, 1, "", 1), "CPU Boost Clock",
|
||||||
this->configList->values[KipConfigValue_marikoCpuUVHigh] ? &mCpuClockThresholdsUV : &mCpuClockThresholds, {}, maxClkOptions,
|
this->configList->values[KipConfigValue_marikoCpuUVHigh] ? &mCpuClockThresholdsUV : &mCpuClockThresholds, {}, maxClkOptions,
|
||||||
false, true);
|
false, true);
|
||||||
@@ -2265,6 +2200,11 @@ class CpuSubmenuGui : public MiscGui {
|
|||||||
NamedValue("3366 MHz", 3366000, "±1800 speedo"),
|
NamedValue("3366 MHz", 3366000, "±1800 speedo"),
|
||||||
NamedValue("3400 MHz", 3400000, "±1820 speedo"),
|
NamedValue("3400 MHz", 3400000, "±1820 speedo"),
|
||||||
};
|
};
|
||||||
|
addConfigToggle(HocClkConfigValue_AutoRAMCPUOverclock, "Auto CPU RAM OC");
|
||||||
|
addConfigButton(HocClkConfigValue_AutoRamCpuCpuOCFreq, "Auto CPU RAM OC CPU clock", ValueRange(0, 0, 1, "", 1), "CPU Clock",
|
||||||
|
&thresholdsDisabled, {}, clkOptionsRamOc, false, false);
|
||||||
|
addConfigButton(HocClkConfigValue_AutoRamCpuRamOCThreshold, "Auto CPU RAM OC Threshold", ValueRange(0, 0, 1, "", 1), "RAM Clock",
|
||||||
|
&thresholdsDisabled, {}, emcMaxClock, false, false);
|
||||||
} else {
|
} else {
|
||||||
addConfigTrackbar(KipConfigValue_eristaCpuUV, "CPU UV", ValueRange(0, 5, 1));
|
addConfigTrackbar(KipConfigValue_eristaCpuUV, "CPU UV", ValueRange(0, 5, 1));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,276 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "update_gui.h"
|
||||||
|
#include "ult_ext.h"
|
||||||
|
#include "base_menu_gui.h"
|
||||||
|
#include "../format.h"
|
||||||
|
#include "../style.h"
|
||||||
|
|
||||||
|
#include <tesla.hpp>
|
||||||
|
#include <ultra.hpp>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr u16 PANEL_H = 80;
|
||||||
|
|
||||||
|
class UpdateStatusPanel : public tsl::elm::Element {
|
||||||
|
public:
|
||||||
|
void setState(int stage, int progress,
|
||||||
|
const std::string &pkg, const std::string &msg) {
|
||||||
|
m_stage = stage;
|
||||||
|
m_progress = progress;
|
||||||
|
m_pkg = pkg;
|
||||||
|
m_msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(tsl::gfx::Renderer *renderer) override {
|
||||||
|
if (this->getHeight() == 0) return;
|
||||||
|
|
||||||
|
const s32 PAD = 20;
|
||||||
|
const s32 x = this->getX() + PAD;
|
||||||
|
const s32 y = this->getY();
|
||||||
|
const s32 w = this->getWidth() - PAD * 2;
|
||||||
|
|
||||||
|
if (m_stage == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_stage == 1 || m_stage == 2) {
|
||||||
|
const char *opLabel = (m_stage == 1) ? "Downloading" : "Extracting";
|
||||||
|
tsl::Color accent = (m_stage == 1)
|
||||||
|
? tsl::Color{0x4, 0x8, 0xF, 0xF} // blue
|
||||||
|
: tsl::Color{0x3, 0xA, 0x8, 0xF}; // teal
|
||||||
|
|
||||||
|
renderer->drawString(opLabel, false, x, y + 20, 15, accent);
|
||||||
|
renderer->drawString(m_pkg.c_str(), false, x + 104, y + 20, 15,
|
||||||
|
tsl::sectionTextColor);
|
||||||
|
|
||||||
|
const s32 barY = y + 48;
|
||||||
|
renderer->drawRect(x, barY, w, 7, tsl::Color{0x3, 0x3, 0x3, 0xF});
|
||||||
|
|
||||||
|
int pct = (m_progress < 0) ? 0 : (m_progress > 100 ? 100 : m_progress);
|
||||||
|
s32 fillW = (s32)((long)w * pct / 100);
|
||||||
|
if (fillW > 0)
|
||||||
|
renderer->drawRect(x, barY, fillW, 7, accent);
|
||||||
|
|
||||||
|
char buf[8];
|
||||||
|
if (m_progress < 0)
|
||||||
|
std::snprintf(buf, sizeof(buf), "...");
|
||||||
|
else
|
||||||
|
std::snprintf(buf, sizeof(buf), "%d%%", pct);
|
||||||
|
|
||||||
|
u32 tw = renderer->getTextDimensions(buf, false, 13).first;
|
||||||
|
renderer->drawString(buf, false,
|
||||||
|
x + w - (s32)tw, barY - 14, 13, tsl::infoTextColor);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tsl::Color col;
|
||||||
|
switch (m_stage) {
|
||||||
|
case 3: col = {0x3, 0xB, 0x5, 0xF}; break; // Done → green
|
||||||
|
case 4: col = {0xE, 0x3, 0x3, 0xF}; break; // Failed → red
|
||||||
|
default: col = {0x7, 0x7, 0x7, 0xF}; break; // Cancel → grey
|
||||||
|
}
|
||||||
|
renderer->drawString(m_msg.c_str(), false, x, y + 30, 14, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void layout(u16, u16, u16, u16) override {
|
||||||
|
this->setBoundaries(this->getX(), this->getY(),
|
||||||
|
this->getWidth(), PANEL_H);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *requestFocus(Element *, tsl::FocusDirection) override {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_stage = 0;
|
||||||
|
int m_progress = 0;
|
||||||
|
std::string m_pkg;
|
||||||
|
std::string m_msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdateGui::UpdateGui() = default;
|
||||||
|
UpdateGui::~UpdateGui() { reapThread(); }
|
||||||
|
|
||||||
|
void UpdateGui::startJob(int packageIndex, bool extractOnly) {
|
||||||
|
if (isBusy()) return;
|
||||||
|
|
||||||
|
m_activePackage = packageIndex;
|
||||||
|
m_extractOnly = extractOnly;
|
||||||
|
m_resultMessage.clear();
|
||||||
|
|
||||||
|
ult::abortDownload.store(false, std::memory_order_release);
|
||||||
|
ult::abortUnzip.store(false, std::memory_order_release);
|
||||||
|
|
||||||
|
m_stage.store(UpdateStage::Downloading, std::memory_order_release);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(threadCreate(&m_thread, jobEntry, this,
|
||||||
|
nullptr, 0x8000, 0x20, -2))) {
|
||||||
|
m_threadActive = true;
|
||||||
|
threadStart(&m_thread);
|
||||||
|
} else {
|
||||||
|
m_resultMessage = "Failed to create thread";
|
||||||
|
m_stage.store(UpdateStage::Failed, std::memory_order_release);
|
||||||
|
m_threadActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateGui::requestCancel() {
|
||||||
|
ult::abortDownload.store(true, std::memory_order_release);
|
||||||
|
ult::abortUnzip.store(true, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateGui::reapThread() {
|
||||||
|
if (m_threadActive) {
|
||||||
|
threadWaitForExit(&m_thread);
|
||||||
|
threadClose(&m_thread);
|
||||||
|
m_threadActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */ void UpdateGui::jobEntry(void *arg) {
|
||||||
|
static_cast<UpdateGui *>(arg)->jobBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateGui::jobBody() {
|
||||||
|
const PackageInfo &pkg = kPackages[m_activePackage];
|
||||||
|
|
||||||
|
if (!m_extractOnly) {
|
||||||
|
m_stage.store(UpdateStage::Downloading, std::memory_order_release);
|
||||||
|
ult::createDirectory("sdmc:/config/horizon-oc/");
|
||||||
|
|
||||||
|
bool ok = ult::downloadFile(pkg.url, pkg.zipPath, false, false);
|
||||||
|
if (!ok || ult::abortDownload.load(std::memory_order_acquire)) {
|
||||||
|
m_resultMessage = ult::abortDownload.load(std::memory_order_acquire)
|
||||||
|
? "Download cancelled"
|
||||||
|
: "Download failed -- check network";
|
||||||
|
m_stage.store(
|
||||||
|
ult::abortDownload.load() ? UpdateStage::Cancelled : UpdateStage::Failed,
|
||||||
|
std::memory_order_release);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stage.store(UpdateStage::Extracting, std::memory_order_release);
|
||||||
|
|
||||||
|
bool ok = ult::unzipFile(pkg.zipPath, "sdmc:/");
|
||||||
|
if (!ok || ult::abortUnzip.load(std::memory_order_acquire)) {
|
||||||
|
m_resultMessage = ult::abortUnzip.load(std::memory_order_acquire)
|
||||||
|
? "Extraction cancelled"
|
||||||
|
: "Extraction failed -- archive may be corrupt";
|
||||||
|
m_stage.store(
|
||||||
|
ult::abortUnzip.load() ? UpdateStage::Cancelled : UpdateStage::Failed,
|
||||||
|
std::memory_order_release);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::remove(pkg.zipPath);
|
||||||
|
|
||||||
|
m_resultMessage = "Update complete! Restart required.";
|
||||||
|
m_stage.store(UpdateStage::Done, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UpdateGui::listUI() {
|
||||||
|
BaseMenuGui::refresh();
|
||||||
|
if (!this->context) return;
|
||||||
|
|
||||||
|
this->listElement->addItem(new tsl::elm::CategoryHeader("Releases"));
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
auto *item = new tsl::elm::ListItem(kPackages[i].displayName);
|
||||||
|
item->setValue("\uE0E0");
|
||||||
|
|
||||||
|
const int idx = i;
|
||||||
|
item->setClickListener([this, idx](u64 keys) -> bool {
|
||||||
|
if (isBusy()) return false;
|
||||||
|
if (keys & HidNpadButton_A) {
|
||||||
|
startJob(idx, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
m_items[i] = item;
|
||||||
|
this->listElement->addItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->listElement->addItem(new tsl::elm::CategoryHeader("Status"));
|
||||||
|
|
||||||
|
auto *panel = new UpdateStatusPanel();
|
||||||
|
panel->setState(0, 0, "", "");
|
||||||
|
m_status = panel;
|
||||||
|
this->listElement->addItem(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateGui::pollJob() {
|
||||||
|
auto stage = m_stage.load(std::memory_order_acquire);
|
||||||
|
if (stage == UpdateStage::Idle || !m_status) return;
|
||||||
|
|
||||||
|
const char *pkgName = (m_activePackage >= 0 && m_activePackage < 2)
|
||||||
|
? kPackages[m_activePackage].displayName : "";
|
||||||
|
|
||||||
|
if (stage == UpdateStage::Downloading) {
|
||||||
|
int pct = ult::downloadPercentage.load(std::memory_order_relaxed);
|
||||||
|
m_status->setState(1, pct, pkgName, "");
|
||||||
|
if (m_items[m_activePackage])
|
||||||
|
m_items[m_activePackage]->setValue("...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage == UpdateStage::Extracting) {
|
||||||
|
int pct = ult::unzipPercentage.load(std::memory_order_relaxed);
|
||||||
|
m_status->setState(2, pct, pkgName, "");
|
||||||
|
if (m_items[m_activePackage])
|
||||||
|
m_items[m_activePackage]->setValue("...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int panelStage = (stage == UpdateStage::Done) ? 3
|
||||||
|
: (stage == UpdateStage::Failed) ? 4 : 5;
|
||||||
|
|
||||||
|
m_status->setState(panelStage, 100, pkgName, m_resultMessage);
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
if (m_items[i]) m_items[i]->setValue("\uE0E0");
|
||||||
|
|
||||||
|
reapThread();
|
||||||
|
m_stage.store(UpdateStage::Idle, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateGui::update() {
|
||||||
|
BaseMenuGui::update();
|
||||||
|
pollJob();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UpdateGui::handleInput(u64 keysDown, u64 keysHeld,
|
||||||
|
const HidTouchState &touchPos,
|
||||||
|
HidAnalogStickState leftJoy,
|
||||||
|
HidAnalogStickState rightJoy) {
|
||||||
|
if ((keysDown & HidNpadButton_R) && isBusy()) {
|
||||||
|
requestCancel();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BaseMenuGui::handleInput(keysDown, keysHeld, touchPos, leftJoy, rightJoy);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UpdateGui::getJumpToItemName() {
|
||||||
|
return "Updates";
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "../../ipc.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base_menu_gui.h"
|
||||||
|
|
||||||
|
class UpdateStatusPanel;
|
||||||
|
|
||||||
|
class UpdateGui : public BaseMenuGui {
|
||||||
|
|
||||||
|
public:
|
||||||
|
UpdateGui();
|
||||||
|
~UpdateGui();
|
||||||
|
|
||||||
|
void listUI() override;
|
||||||
|
void update() override;
|
||||||
|
bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos,
|
||||||
|
HidAnalogStickState leftJoy, HidAnalogStickState rightJoy) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class UpdateStage {
|
||||||
|
Idle,
|
||||||
|
Downloading,
|
||||||
|
Extracting,
|
||||||
|
Done,
|
||||||
|
Failed,
|
||||||
|
Cancelled,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PackageInfo {
|
||||||
|
const char *displayName;
|
||||||
|
const char *url;
|
||||||
|
const char *zipPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr PackageInfo kPackages[2] = {
|
||||||
|
{"Horizon OC", "https://github.com/Horizon-OC/Horizon-OC/releases/latest/download/dist.zip", "sdmc:/config/horizon-oc/dist.zip"},
|
||||||
|
{"Horizon OC + Extensions", "https://github.com/Horizon-OC/Horizon-OC/releases/latest/download/dist_ext.zip", "sdmc:/config/horizon-oc/dist_ext.zip"},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool isBusy() const {
|
||||||
|
return m_stage.load(std::memory_order_acquire) != UpdateStage::Idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startJob(int packageIndex, bool extractOnly);
|
||||||
|
void requestCancel();
|
||||||
|
void reapThread();
|
||||||
|
void pollJob();
|
||||||
|
|
||||||
|
static void jobEntry(void *arg);
|
||||||
|
void jobBody();
|
||||||
|
|
||||||
|
std::atomic<UpdateStage> m_stage{UpdateStage::Idle};
|
||||||
|
int m_activePackage = -1;
|
||||||
|
bool m_extractOnly = false;
|
||||||
|
bool m_threadActive = false;
|
||||||
|
Thread m_thread{};
|
||||||
|
std::string m_resultMessage;
|
||||||
|
|
||||||
|
tsl::elm::ListItem *m_items[2] = {nullptr, nullptr};
|
||||||
|
UpdateStatusPanel *m_status = nullptr;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string getJumpToItemName() override;
|
||||||
|
};
|
||||||
@@ -28,8 +28,8 @@ INCLUDES := ../common/include src/hos src/soc src/i2c src/util src/pwr src/ipc
|
|||||||
EXEFS_SRC := exefs_src
|
EXEFS_SRC := exefs_src
|
||||||
LIBNAMES := minIni
|
LIBNAMES := minIni
|
||||||
# major minor patch
|
# major minor patch
|
||||||
TARGET_VERSION := 2.6.0
|
TARGET_VERSION := 3.0.0
|
||||||
KIP_VERSION := 260
|
KIP_VERSION := 300
|
||||||
CUST_REV := 7
|
CUST_REV := 7
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -49,7 +49,6 @@
|
|||||||
#include "board_volt.hpp"
|
#include "board_volt.hpp"
|
||||||
#include <ipc_server.h>
|
#include <ipc_server.h>
|
||||||
#include <lockable_mutex.h>
|
#include <lockable_mutex.h>
|
||||||
#include "../soc/dram_mrr.hpp"
|
|
||||||
|
|
||||||
namespace board {
|
namespace board {
|
||||||
|
|
||||||
@@ -239,10 +238,6 @@ namespace board {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u8 GetDramID() {
|
u8 GetDramID() {
|
||||||
return soc::mrr::IdentifyDramId();
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 GetFuseDramId() {
|
|
||||||
return gDramID;
|
return gDramID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ namespace board {
|
|||||||
HocClkSocType GetSocType();
|
HocClkSocType GetSocType();
|
||||||
HocClkConsoleType GetConsoleType();
|
HocClkConsoleType GetConsoleType();
|
||||||
u8 GetDramID();
|
u8 GetDramID();
|
||||||
u8 GetFuseDramId();
|
|
||||||
u8 GetGpuSpeedoBracket();
|
u8 GetGpuSpeedoBracket();
|
||||||
bool IsDram8GB();
|
bool IsDram8GB();
|
||||||
void SetDisplayRefreshDockedState(bool docked);
|
void SetDisplayRefreshDockedState(bool docked);
|
||||||
|
|||||||
@@ -83,6 +83,24 @@ namespace fileUtils {
|
|||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
|
|
||||||
|
char buff[0xfff];
|
||||||
|
int len = vsnprintf(buff, sizeof(buff), format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Leave room for the newline + NUL.
|
||||||
|
if ((size_t)len >= sizeof(buff) - 1) {
|
||||||
|
len = sizeof(buff) - 2;
|
||||||
|
}
|
||||||
|
buff[len++] = '\n';
|
||||||
|
buff[len] = '\0';
|
||||||
|
|
||||||
|
// Debug UART log
|
||||||
|
svcOutputDebugString(buff, len);
|
||||||
|
|
||||||
if (g_has_initialized) {
|
if (g_has_initialized) {
|
||||||
RefreshFlags(false);
|
RefreshFlags(false);
|
||||||
|
|
||||||
@@ -93,14 +111,11 @@ namespace fileUtils {
|
|||||||
timespec now = {};
|
timespec now = {};
|
||||||
clock_gettime(CLOCK_REALTIME, &now);
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
|
||||||
fprintf(file, "[%luls] ", now.tv_sec - bootTimeS);
|
fprintf(file, "[%luls] %s", now.tv_sec - bootTimeS, buff);
|
||||||
vfprintf(file, format, args);
|
|
||||||
fprintf(file, "\n");
|
|
||||||
fclose(file);
|
fclose(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
va_end(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WriteContextToCsv(const HocClkContext *context) {
|
void WriteContextToCsv(const HocClkContext *context) {
|
||||||
|
|||||||
@@ -222,7 +222,8 @@ namespace kip {
|
|||||||
!config::GetConfigValue(HocClkConfigValue_IsFirstLoad)) {
|
!config::GetConfigValue(HocClkConfigValue_IsFirstLoad)) {
|
||||||
MigrateKipData(cust_get_cust_rev(&table), cust_get_kip_version(&table));
|
MigrateKipData(cust_get_cust_rev(&table), cust_get_kip_version(&table));
|
||||||
SetKipData();
|
SetKipData();
|
||||||
notification::writeNotification("Horizon OC\nKIP has been updated\nPlease reboot your console");
|
clockManager::gContext.rebootRequired = true;
|
||||||
|
notification::writeNotification("Horizon OC\nKIP has been updated\nPlease reboot your console to use Horizon OC");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (config::GetConfigValue(HocClkConfigValue_IsFirstLoad) == true) {
|
if (config::GetConfigValue(HocClkConfigValue_IsFirstLoad) == true) {
|
||||||
|
|||||||
@@ -295,6 +295,47 @@ namespace clockManager {
|
|||||||
hz++;
|
hz++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Since it is a pain to patch the vtables in ipc we can just hack the freqs in. You can still set them. */
|
||||||
|
constexpr u64 EmcClkOSLimitHz = 1600000ULL * 1000; // 1600 MHz
|
||||||
|
const u64 maxHz = static_cast<u64>(config::GetConfigValue(KipConfigValue_marikoEmcMaxClock)) * 1000;
|
||||||
|
if (module == HocClkModule_MEM && board::GetSocType() == HocClkSocType_Mariko &&
|
||||||
|
kip::kipAvailable && maxHz >= EmcClkOSLimitHz &&
|
||||||
|
config::GetConfigValue(KipConfigValue_stepMode) == 4 /* 33 MHz */) {
|
||||||
|
|
||||||
|
/* Drop the clkrst entries above the OS limit */
|
||||||
|
u32 kept = 0;
|
||||||
|
for (u32 i = 0; i < gFreqTable[module].count; ++i) {
|
||||||
|
if (static_cast<u64>(gFreqTable[module].list[i]) <= EmcClkOSLimitHz) {
|
||||||
|
gFreqTable[module].list[kept++] = gFreqTable[module].list[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gFreqTable[module].count = kept;
|
||||||
|
|
||||||
|
auto push = [&](u64 freqHz) {
|
||||||
|
if (freqHz > maxHz || gFreqTable[module].count >= HOCCLK_FREQ_LIST_MAX) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gFreqTable[module].list[gFreqTable[module].count++] = static_cast<u32>(freqHz);
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u32 stepFreqs33[] = {
|
||||||
|
1633000, 1666000, 1700000, 1733000, 1766000, 1800000, 1833000, 1866000, 1900000, 1933000,
|
||||||
|
1966000, 2000000, 2033000, 2066000, 2100000, 2133000, 2166000, 2200000, 2233000, 2266000,
|
||||||
|
2300000, 2333000, 2366000, 2400000, 2433000, 2466000, 2500000, 2533000, 2566000, 2600000,
|
||||||
|
2633000, 2666000, 2700000, 2733000, 2766000, 2800000, 2833000, 2866000, 2900000, 2933000,
|
||||||
|
2966000, 3000000, 3033000, 3066000, 3100000, 3133000, 3166000, 3200000, 3233000, 3266000,
|
||||||
|
3300000, 3333000, 3366000, 3400000, 3433000, 3466000, 3500000,
|
||||||
|
};
|
||||||
|
for (u32 f : stepFreqs33) {
|
||||||
|
push(static_cast<u64>(f) * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gFreqTable[module].count == 0 ||
|
||||||
|
static_cast<u64>(gFreqTable[module].list[gFreqTable[module].count - 1]) != maxHz) {
|
||||||
|
push(maxHz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fileUtils::LogLine("[mgr] count = %u", gFreqTable[module].count);
|
fileUtils::LogLine("[mgr] count = %u", gFreqTable[module].count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +603,6 @@ namespace clockManager {
|
|||||||
if (module == HocClkModule_MEM && targetHz > oldHz && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
|
if (module == HocClkModule_MEM && targetHz > oldHz && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) {
|
||||||
ApplyGpuDvfs(targetHz);
|
ApplyGpuDvfs(targetHz);
|
||||||
}
|
}
|
||||||
|
|
||||||
board::SetHz((HocClkModule)module, nearestHz);
|
board::SetHz((HocClkModule)module, nearestHz);
|
||||||
gContext.freqs[module] = nearestHz;
|
gContext.freqs[module] = nearestHz;
|
||||||
|
|
||||||
@@ -741,6 +781,11 @@ namespace clockManager {
|
|||||||
gContext = {};
|
gContext = {};
|
||||||
gContext.applicationId = 0;
|
gContext.applicationId = 0;
|
||||||
gContext.profile = HocClkProfile_Handheld;
|
gContext.profile = HocClkProfile_Handheld;
|
||||||
|
|
||||||
|
/* Load the KIP customize table before building the freq tables: the MEM freq-list
|
||||||
|
synthesis in RefreshFreqTableRow reads marikoEmcMaxClock / stepMode from it. */
|
||||||
|
kip::GetKipData();
|
||||||
|
|
||||||
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) {
|
||||||
gContext.freqs[module] = 0;
|
gContext.freqs[module] = 0;
|
||||||
gContext.realFreqs[module] = 0;
|
gContext.realFreqs[module] = 0;
|
||||||
@@ -759,8 +804,6 @@ namespace clockManager {
|
|||||||
gLastTempLogNs = 0;
|
gLastTempLogNs = 0;
|
||||||
gLastCsvWriteNs = 0;
|
gLastCsvWriteNs = 0;
|
||||||
|
|
||||||
kip::GetKipData();
|
|
||||||
|
|
||||||
board::FuseData *fuse = board::GetFuseData();
|
board::FuseData *fuse = board::GetFuseData();
|
||||||
|
|
||||||
gContext.speedos[HocClkSpeedo_CPU] = fuse->cpuSpeedo;
|
gContext.speedos[HocClkSpeedo_CPU] = fuse->cpuSpeedo;
|
||||||
|
|||||||
@@ -1,393 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) CtCaer
|
|
||||||
*
|
|
||||||
* Copyright (c) Souldbminer
|
|
||||||
*
|
|
||||||
* 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 "dram_mrr.hpp"
|
|
||||||
#include "../board/board.hpp"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <switch.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
namespace soc::mrr {
|
|
||||||
constexpr u64 EmcPhysBase = 0x7001B000;
|
|
||||||
constexpr u64 Emc0PhysBase = 0x7001E000;
|
|
||||||
constexpr u64 Emc1PhysBase = 0x7001F000;
|
|
||||||
|
|
||||||
constexpr u32 EmcRegMrr = 0xEC;
|
|
||||||
constexpr u32 EmcRegAdrCfg = 0x10;
|
|
||||||
constexpr u32 EmcRegFbioCfg7 = 0x584;
|
|
||||||
constexpr u32 EmcRegEmcStatus = 0x2B4;
|
|
||||||
constexpr u32 EmcStatusMrrDivld = (1u << 20);
|
|
||||||
constexpr int EmcStatusUpdateTimeout = 100000;
|
|
||||||
|
|
||||||
static bool SmcRead(u64 phys_addr, u32 *out) {
|
|
||||||
SecmonArgs args = {};
|
|
||||||
args.X[0] = 0xF0000002;
|
|
||||||
args.X[1] = phys_addr;
|
|
||||||
args.X[2] = 0;
|
|
||||||
args.X[3] = 0;
|
|
||||||
svcCallSecureMonitor(&args);
|
|
||||||
if (args.X[1] == phys_addr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*out = static_cast<u32>(args.X[1]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool SmcWrite(u64 phys_addr, u32 value) {
|
|
||||||
SecmonArgs args = {};
|
|
||||||
args.X[0] = 0xF0000002;
|
|
||||||
args.X[1] = phys_addr;
|
|
||||||
args.X[2] = 0xFFFFFFFF;
|
|
||||||
args.X[3] = value;
|
|
||||||
svcCallSecureMonitor(&args);
|
|
||||||
return args.X[1] != phys_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool EmcRead(u32 offset, u32 *out) { return SmcRead(EmcPhysBase + offset, out); }
|
|
||||||
static bool EmcWrite(u32 offset, u32 value) { return SmcWrite(EmcPhysBase + offset, value); }
|
|
||||||
static bool Emc0Read(u32 offset, u32 *out) { return SmcRead(Emc0PhysBase + offset, out); }
|
|
||||||
static bool Emc1Read(u32 offset, u32 *out) { return SmcRead(Emc1PhysBase + offset, out); }
|
|
||||||
|
|
||||||
/* 0 = ignore */
|
|
||||||
|
|
||||||
const DramRevisionMap rev[DRAM_COUNT] = {
|
|
||||||
/* Erista */
|
|
||||||
{ .soc = HocClkSocType_Erista, .mfg = MFG_Samsung, .major = 0, .minor = 0, .density = 0, .densityCount = 0 }, /* HB-MGCH */
|
|
||||||
{ .soc = HocClkSocType_Erista, .mfg = MFG_SKHynix, .major = 0, .minor = 0, .density = 0, .densityCount = 0 }, /* NLE */
|
|
||||||
{ .soc = HocClkSocType_Erista, .mfg = MFG_Micron, .major = 0, .minor = 0, .density = 0, .densityCount = 0 }, /* WT:C */
|
|
||||||
|
|
||||||
/* Mariko */
|
|
||||||
/* Samsung */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_Samsung, .major = 6, .minor = 0x10, .density = 1, .densityCount = 2 }, /* AM-MGCJ (6.10, 4GB) */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_Samsung, .major = 7, .minor = 0, .density = 1, .densityCount = 2 }, /* AA-MGCL (7.00, 4GB) */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_Samsung, .major = 8, .minor = 0, .density = 1, .densityCount = 2 }, /* AB-MGCL (8.00, 4GB) */
|
|
||||||
|
|
||||||
/* Micron */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_Micron, .major = 4, .minor = 0x10, .density = 1, .densityCount = 2 }, /* WT:E (4.10, 4GB) */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_Micron, .major = 5, .minor = 0, .density = 1, .densityCount = 2 }, /* WT:F (5.00, 4GB) */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_Micron, .major = 7, .minor = 0, .density = 1, .densityCount = 2 }, /* WT:B (7.00, 4GB) */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_Micron, .major = 7, .minor = 0, .density = 1, .densityCount = 4 }, /* WT:B (7.00, 8GB) */
|
|
||||||
|
|
||||||
/* SK Hynix */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_SKHynix, .major = 4, .minor = 0, .density = 1, .densityCount = 2 }, /* NME (4.00, 4GB) */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_SKHynix, .major = 6, .minor = 0, .density = 1, .densityCount = 2 }, /* NEE (6.00, 4GB) */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_SKHynix, .major = 6, .minor = 0, .density = 1, .densityCount = 4 }, /* NEE (6.00, 8GB) */
|
|
||||||
{ .soc = HocClkSocType_Mariko, .mfg = MFG_SKHynix, .major = 8, .minor = 0, .density = 1, .densityCount = 2 }, /* x267 (8.00, 4GB) */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Ported from hekate */
|
|
||||||
|
|
||||||
bool IsMrrAvailable() {
|
|
||||||
u32 val = 0;
|
|
||||||
return EmcRead(EmcRegAdrCfg, &val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int _sdram_wait_emc_status(u32 reg_offset, u32 bit_mask, bool updated_state, s32 emc_channel)
|
|
||||||
{
|
|
||||||
for (s32 i = 0; i < EmcStatusUpdateTimeout; i++)
|
|
||||||
{
|
|
||||||
u32 val = 0;
|
|
||||||
bool ok;
|
|
||||||
if (emc_channel)
|
|
||||||
{
|
|
||||||
if (emc_channel != 1)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
ok = Emc1Read(reg_offset, &val);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ok = EmcRead(reg_offset, &val);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok && (((val & bit_mask) != 0) == updated_state))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
usleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _sdram_req_mrr_data(u32 data, bool dual_channel)
|
|
||||||
{
|
|
||||||
if (!EmcWrite(EmcRegMrr, data))
|
|
||||||
return false;
|
|
||||||
if (_sdram_wait_emc_status(EmcRegEmcStatus, EmcStatusMrrDivld, true, EMC_CHAN0) != 0)
|
|
||||||
return false;
|
|
||||||
if (dual_channel && _sdram_wait_emc_status(EmcRegEmcStatus, EmcStatusMrrDivld, true, EMC_CHAN1) != 0)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EmcTopology {
|
|
||||||
u32 ranks;
|
|
||||||
u32 channels;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool QueryTopology(EmcTopology *topo) {
|
|
||||||
u32 tmp = 0;
|
|
||||||
if (!EmcRead(EmcRegAdrCfg, &tmp))
|
|
||||||
return false;
|
|
||||||
topo->ranks = (tmp & 1) + 1;
|
|
||||||
if (!EmcRead(EmcRegFbioCfg7, &tmp))
|
|
||||||
return false;
|
|
||||||
u32 ch = (tmp >> 1) & 3;
|
|
||||||
topo->channels = (ch & 1) + ((ch & 2) >> 1);
|
|
||||||
return topo->channels != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ClearMrrLeftovers() {
|
|
||||||
u32 tmp = 0;
|
|
||||||
for (u32 i = 0; i < 16; i++)
|
|
||||||
{
|
|
||||||
(void)EmcRead(EmcRegMrr, &tmp);
|
|
||||||
usleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void StoreMrrBytes(u32 mrr, u8 *lo, u8 *hi) {
|
|
||||||
*lo = mrr & 0xFF;
|
|
||||||
*hi = (mrr >> 8) & 0xFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ReadMrxRank(emc_mr_t mrx, bool dual_rank, bool dual_channel, emc_mr_data_t *out) {
|
|
||||||
memset(out, 0xFF, sizeof(*out));
|
|
||||||
u32 mrr = 0;
|
|
||||||
|
|
||||||
// Get Device 0 (Rank 0) info from both dram chips (channels).
|
|
||||||
if (!_sdram_req_mrr_data((2u << 30) | (static_cast<u32>(mrx) << 16), dual_channel))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Ram module 0 info.
|
|
||||||
if (!Emc0Read(EmcRegMrr, &mrr))
|
|
||||||
return false;
|
|
||||||
StoreMrrBytes(mrr, &out->chip0.rank0_ch0, &out->chip0.rank0_ch1);
|
|
||||||
|
|
||||||
// Ram module 1 info.
|
|
||||||
if (dual_channel)
|
|
||||||
{
|
|
||||||
if (!Emc1Read(EmcRegMrr, &mrr))
|
|
||||||
return false;
|
|
||||||
StoreMrrBytes(mrr, &out->chip1.rank0_ch0, &out->chip1.rank0_ch1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If Rank 1 exists, get info.
|
|
||||||
if (dual_rank)
|
|
||||||
{
|
|
||||||
// Get Device 1 (Rank 1) info from both dram chips (channels).
|
|
||||||
if (!_sdram_req_mrr_data((1u << 30) | (static_cast<u32>(mrx) << 16), dual_channel))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Ram module 0 info.
|
|
||||||
if (!Emc0Read(EmcRegMrr, &mrr))
|
|
||||||
return false;
|
|
||||||
StoreMrrBytes(mrr, &out->chip0.rank1_ch0, &out->chip0.rank1_ch1);
|
|
||||||
|
|
||||||
// Ram module 1 info.
|
|
||||||
if (dual_channel)
|
|
||||||
{
|
|
||||||
if (!Emc1Read(EmcRegMrr, &mrr))
|
|
||||||
return false;
|
|
||||||
StoreMrrBytes(mrr, &out->chip1.rank1_ch0, &out->chip1.rank1_ch1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ReadMrxSet(emc_mr_data_t *vendor, emc_mr_data_t *rev0, emc_mr_data_t *rev1,
|
|
||||||
emc_mr_data_t *density, EmcTopology *topo) {
|
|
||||||
EmcTopology t = {};
|
|
||||||
if (!QueryTopology(&t))
|
|
||||||
return false;
|
|
||||||
const bool dual_rank = t.ranks > 1;
|
|
||||||
const bool dual_channel = t.channels > 1;
|
|
||||||
|
|
||||||
ClearMrrLeftovers();
|
|
||||||
|
|
||||||
if (!ReadMrxRank(MR5_MAN_ID, dual_rank, dual_channel, vendor))
|
|
||||||
return false;
|
|
||||||
if (!ReadMrxRank(MR6_REV_ID1, dual_rank, dual_channel, rev0))
|
|
||||||
return false;
|
|
||||||
if (!ReadMrxRank(MR7_REV_ID2, dual_rank, dual_channel, rev1))
|
|
||||||
return false;
|
|
||||||
if (!ReadMrxRank(MR8_DENSITY, dual_rank, dual_channel, density))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*topo = t;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
emc_mr_data_t sdram_read_mrx(emc_mr_t mrx)
|
|
||||||
{
|
|
||||||
emc_mr_data_t data;
|
|
||||||
memset(&data, 0xFF, sizeof(emc_mr_data_t));
|
|
||||||
|
|
||||||
EmcTopology topo = {};
|
|
||||||
if (!QueryTopology(&topo))
|
|
||||||
return data;
|
|
||||||
|
|
||||||
ClearMrrLeftovers();
|
|
||||||
|
|
||||||
(void)ReadMrxRank(mrx, topo.ranks > 1, topo.channels > 1, &data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadRamMr4(u8 *mr4) {
|
|
||||||
EmcTopology topo = {};
|
|
||||||
if (!QueryTopology(&topo))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
ClearMrrLeftovers();
|
|
||||||
|
|
||||||
emc_mr_data_t data = {};
|
|
||||||
if (!ReadMrxRank(MR4_TEMP, topo.ranks > 1, topo.channels > 1, &data))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*mr4 = data.chip0.rank0_ch0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* MR8 density field (bits 5:2) to MB */
|
|
||||||
static bool DecodeDensityMb(u8 mr8, u32 *mb) {
|
|
||||||
switch ((mr8 & 0x3C) >> 2)
|
|
||||||
{
|
|
||||||
case 2: *mb = 512; break;
|
|
||||||
case 3: *mb = 768; break;
|
|
||||||
case 4: *mb = 1024; break;
|
|
||||||
case 5: *mb = 1536; break;
|
|
||||||
case 6: *mb = 2048; break;
|
|
||||||
default: return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool MatchModule(HocClkSocType soc, DramMfg mfg, u8 major, u8 minor,
|
|
||||||
u32 densityMb, u32 densityCount, RealDramModule *mod) {
|
|
||||||
for (int i = 0; i < DRAM_COUNT; i++)
|
|
||||||
{
|
|
||||||
const DramRevisionMap &e = rev[i];
|
|
||||||
if (e.soc != soc || e.mfg != mfg)
|
|
||||||
continue;
|
|
||||||
if (soc != HocClkSocType_Erista)
|
|
||||||
{
|
|
||||||
if (e.major != major || e.minor != minor)
|
|
||||||
continue;
|
|
||||||
if (e.density * 1024u != densityMb)
|
|
||||||
continue;
|
|
||||||
if (e.densityCount != densityCount)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*mod = static_cast<RealDramModule>(i);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u8 FuseIdForModule(RealDramModule mod, HocClkConsoleType console, u8 fuseFallback) {
|
|
||||||
switch (mod)
|
|
||||||
{
|
|
||||||
case DRAM_HBMGCH:
|
|
||||||
case DRAM_NLE:
|
|
||||||
case DRAM_WTC:
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_AMMGCJ:
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 12;
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 8;
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_AAMGCL:
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 17;
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 19;
|
|
||||||
if (console == HocClkConsoleType_Aula) return 24;
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_ABMGCL:
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 20;
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 21;
|
|
||||||
if (console == HocClkConsoleType_Aula) return 22;
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_WTE:
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 11;
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 15;
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_WTF:
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 25;
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 26;
|
|
||||||
if (console == HocClkConsoleType_Aula) return 27;
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_WTB:
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 32;
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 33;
|
|
||||||
if (console == HocClkConsoleType_Aula) return 34;
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_WTB_8GB:
|
|
||||||
case DRAM_NEE_8GB:
|
|
||||||
/* Used in atmosphere and commonly burnt for 8gb upgrade */
|
|
||||||
return 28;
|
|
||||||
case DRAM_NME:
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 10;
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 14;
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_NEE:
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 6;
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 3;
|
|
||||||
if (console == HocClkConsoleType_Aula) return 5;
|
|
||||||
return fuseFallback;
|
|
||||||
case DRAM_X267:
|
|
||||||
if (console == HocClkConsoleType_Iowa) return 29;
|
|
||||||
if (console == HocClkConsoleType_Hoag) return 30;
|
|
||||||
if (console == HocClkConsoleType_Aula) return 31;
|
|
||||||
return fuseFallback;
|
|
||||||
default:
|
|
||||||
return fuseFallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 IdentifyDramId() {
|
|
||||||
const u8 fuseId = board::GetFuseDramId();
|
|
||||||
const HocClkSocType soc = board::GetSocType();
|
|
||||||
|
|
||||||
if (soc == HocClkSocType_Erista)
|
|
||||||
return fuseId;
|
|
||||||
|
|
||||||
if (!IsMrrAvailable())
|
|
||||||
return fuseId;
|
|
||||||
|
|
||||||
emc_mr_data_t vendor = {}, rev0 = {}, rev1 = {}, density = {};
|
|
||||||
EmcTopology topo = {};
|
|
||||||
if (!ReadMrxSet(&vendor, &rev0, &rev1, &density, &topo))
|
|
||||||
return fuseId;
|
|
||||||
|
|
||||||
DramMfg mfg = static_cast<DramMfg>(vendor.chip0.rank0_ch0);
|
|
||||||
|
|
||||||
u32 densityMb = 0;
|
|
||||||
if (!DecodeDensityMb(density.chip0.rank0_ch0, &densityMb))
|
|
||||||
return fuseId;
|
|
||||||
|
|
||||||
RealDramModule mod = DRAM_COUNT;
|
|
||||||
if (!MatchModule(soc, mfg, rev0.chip0.rank0_ch0, rev1.chip0.rank0_ch0,
|
|
||||||
densityMb, topo.ranks * topo.channels, &mod))
|
|
||||||
return fuseId;
|
|
||||||
|
|
||||||
return FuseIdForModule(mod, board::GetConsoleType(), fuseId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) CtCaer
|
|
||||||
*
|
|
||||||
* Copyright (c) Souldbminer
|
|
||||||
*
|
|
||||||
* 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 <hocclk.h>
|
|
||||||
|
|
||||||
namespace soc::mrr {
|
|
||||||
typedef enum RealDramModule {
|
|
||||||
/* Erista */
|
|
||||||
DRAM_HBMGCH = 0,
|
|
||||||
DRAM_NLE,
|
|
||||||
DRAM_WTC,
|
|
||||||
|
|
||||||
/* Mariko */
|
|
||||||
|
|
||||||
/* Samsung */
|
|
||||||
DRAM_AMMGCJ,
|
|
||||||
DRAM_AAMGCL,
|
|
||||||
DRAM_ABMGCL,
|
|
||||||
|
|
||||||
/* Micron */
|
|
||||||
DRAM_WTE,
|
|
||||||
DRAM_WTF,
|
|
||||||
DRAM_WTB,
|
|
||||||
DRAM_WTB_8GB,
|
|
||||||
|
|
||||||
/* SK Hynix */
|
|
||||||
DRAM_NME,
|
|
||||||
DRAM_NEE,
|
|
||||||
DRAM_NEE_8GB,
|
|
||||||
DRAM_X267,
|
|
||||||
DRAM_COUNT,
|
|
||||||
} RealDramModule;
|
|
||||||
|
|
||||||
typedef enum _emc_mr_t
|
|
||||||
{
|
|
||||||
MR0_FEAT = 0,
|
|
||||||
MR4_TEMP = 4,
|
|
||||||
MR5_MAN_ID = 5,
|
|
||||||
MR6_REV_ID1 = 6,
|
|
||||||
MR7_REV_ID2 = 7,
|
|
||||||
MR8_DENSITY = 8,
|
|
||||||
} emc_mr_t;
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
EMC_CHAN0 = 0,
|
|
||||||
EMC_CHAN1 = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _emc_mr_chip_data_t
|
|
||||||
{
|
|
||||||
// Device 0.
|
|
||||||
u8 rank0_ch0;
|
|
||||||
u8 rank0_ch1;
|
|
||||||
|
|
||||||
// Device 1.
|
|
||||||
u8 rank1_ch0;
|
|
||||||
u8 rank1_ch1;
|
|
||||||
} emc_mr_chip_data_t;
|
|
||||||
|
|
||||||
typedef struct _emc_mr_data_t
|
|
||||||
{
|
|
||||||
emc_mr_chip_data_t chip0;
|
|
||||||
emc_mr_chip_data_t chip1;
|
|
||||||
} emc_mr_data_t;
|
|
||||||
|
|
||||||
typedef enum DramMfg {
|
|
||||||
MFG_Samsung = 1,
|
|
||||||
MFG_SKHynix = 6,
|
|
||||||
MFG_Micron = 255,
|
|
||||||
} DramMfg;
|
|
||||||
|
|
||||||
typedef struct DramRevisionMap {
|
|
||||||
HocClkSocType soc;
|
|
||||||
DramMfg mfg;
|
|
||||||
u8 major;
|
|
||||||
u8 minor;
|
|
||||||
u8 density; // GB per die
|
|
||||||
u8 densityCount; // number of dies (2 = 4GB, 4 = 8GB)
|
|
||||||
} DramRevisionMap;
|
|
||||||
|
|
||||||
extern const DramRevisionMap rev[DRAM_COUNT];
|
|
||||||
|
|
||||||
emc_mr_data_t sdram_read_mrx(emc_mr_t mrx);
|
|
||||||
bool IsMrrAvailable();
|
|
||||||
|
|
||||||
bool ReadRamMr4(u8 *mr4);
|
|
||||||
|
|
||||||
u8 IdentifyDramId();
|
|
||||||
}
|
|
||||||
@@ -27,12 +27,12 @@ namespace gm20b {
|
|||||||
#define GPU_SIZE 0x1000000
|
#define GPU_SIZE 0x1000000
|
||||||
#define GPU_TRIM_SYS_GPCPLL_CFG 0x00
|
#define GPU_TRIM_SYS_GPCPLL_CFG 0x00
|
||||||
#define GPU_TRIM_SYS_GPCPLL_COEFF 0x04
|
#define GPU_TRIM_SYS_GPCPLL_COEFF 0x04
|
||||||
#define GPU_TRIM_SYS_GPCPLL_CFG2 0x0C
|
#define GPU_TRIM_SYS_GPCPLL_CFG2 0x08
|
||||||
#define GPU_TRIM_SYS_GPCPLL_CFG3 0x18
|
#define GPU_TRIM_SYS_GPCPLL_CFG3 0x0C
|
||||||
#define GPU_TRIM_SYS_GPCPLL_DVFS0 0x10
|
#define GPU_TRIM_SYS_GPCPLL_DVFS0 0x10
|
||||||
#define GPU_TRIM_SYS_GPCPLL_DVFS1 0x14
|
#define GPU_TRIM_SYS_GPCPLL_DVFS1 0x14
|
||||||
#define GPU_TRIM_SYS_GPCPLL_NDIV_SLOWDOWN 0x1C
|
#define GPU_TRIM_SYS_GPCPLL_NDIV_SLOWDOWN 0x1C
|
||||||
#define GPU_TRIM_SYS_SEL_VCO 0x100
|
#define GPU_TRIM_SYS_SEL_VCO 0x2C
|
||||||
#define GPU_TRIM_SYS_GPC2CLK_OUT 0x250
|
#define GPU_TRIM_SYS_GPC2CLK_OUT 0x250
|
||||||
|
|
||||||
#define GPC_BCAST(x) (*(volatile u32 *)(gpu_base + 0x132800ul + (x)))
|
#define GPC_BCAST(x) (*(volatile u32 *)(gpu_base + 0x132800ul + (x)))
|
||||||
@@ -47,7 +47,7 @@ namespace gm20b {
|
|||||||
#define GPCPLL_CFG_LOCK BIT(17)
|
#define GPCPLL_CFG_LOCK BIT(17)
|
||||||
|
|
||||||
#define GPCPLL_CFG2_SDM_DIN_MASK 0x000000FFu
|
#define GPCPLL_CFG2_SDM_DIN_MASK 0x000000FFu
|
||||||
#define GPCPLL_CFG2_SDM_DIN_NEW_MASK 0x0000FF00u
|
#define GPCPLL_CFG2_SDM_DIN_NEW_MASK 0x007FFF00u
|
||||||
#define GPCPLL_CFG2_STEPA_SHIFT 24
|
#define GPCPLL_CFG2_STEPA_SHIFT 24
|
||||||
#define GPCPLL_CFG2_STEPA_MASK 0xFF000000u
|
#define GPCPLL_CFG2_STEPA_MASK 0xFF000000u
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ namespace gm20b {
|
|||||||
#define GPCPLL_DVFS0_DFS_COEFF_MASK 0x0000007Fu
|
#define GPCPLL_DVFS0_DFS_COEFF_MASK 0x0000007Fu
|
||||||
|
|
||||||
#define NDIV_SLOWDOWN_SLOWDOWN_USING_PLL BIT(22)
|
#define NDIV_SLOWDOWN_SLOWDOWN_USING_PLL BIT(22)
|
||||||
#define NDIV_SLOWDOWN_EN_DYNRAMP BIT(31)
|
#define NDIV_SLOWDOWN_EN_DYNRAMP BIT(23)
|
||||||
|
|
||||||
#define DYNRAMP_DONE_SYNCED BIT(24)
|
#define DYNRAMP_DONE_SYNCED BIT(24)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
+7
-9
@@ -30,15 +30,13 @@ fi
|
|||||||
|
|
||||||
echo
|
echo
|
||||||
echo "*** Patching exosphere ***"
|
echo "*** Patching exosphere ***"
|
||||||
cp -v "$EXO_SRC/secmon_define_emc_access_table.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_emc_access_table_data.inc" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_emc_access_table_data.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_define_emc_access_table.inc" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_define_emc01_access_table.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_rtc_pmc_access_table_data.inc" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_emc01_access_table_data.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_define_rtc_pmc_access_table.inc" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_define_rtc_pmc_access_table.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_smc_register_access.cpp" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_rtc_pmc_access_table_data.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_smc_handler.cpp" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_smc_register_access.cpp" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_memory_layout.hpp" "$LIBEXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_smc_handler.cpp" "$EXO_DEST/"
|
|
||||||
cp -v "$EXO_SRC/secmon_memory_layout.hpp" "$LIBEXO_DEST/"
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "*** Compiling exosphere ***"
|
echo "*** Compiling exosphere ***"
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
EXT=0
|
EXT=0
|
||||||
LDR_MAKE="nx_release"
|
LDR_MAKE="nx_release"
|
||||||
|
LDR_SET=0
|
||||||
NO_EXO=0
|
NO_EXO=0
|
||||||
|
JOBS=""
|
||||||
|
UART_LOGGING=0
|
||||||
|
|
||||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
DIST_DIR="$ROOT_DIR/dist"
|
DIST_DIR="$ROOT_DIR/dist"
|
||||||
@@ -14,10 +17,21 @@ while [ $# -gt 0 ]; do
|
|||||||
;;
|
;;
|
||||||
--ldr=*)
|
--ldr=*)
|
||||||
LDR_MAKE="${1#*=}"
|
LDR_MAKE="${1#*=}"
|
||||||
|
LDR_SET=1
|
||||||
|
;;
|
||||||
|
-u|--uart)
|
||||||
|
UART_LOGGING=1
|
||||||
;;
|
;;
|
||||||
--no-exo)
|
--no-exo)
|
||||||
NO_EXO=1
|
NO_EXO=1
|
||||||
;;
|
;;
|
||||||
|
-j)
|
||||||
|
shift
|
||||||
|
JOBS="$1"
|
||||||
|
;;
|
||||||
|
-j*)
|
||||||
|
JOBS="${1#-j}"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unknown option: $1"
|
echo "Unknown option: $1"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -26,6 +40,10 @@ while [ $# -gt 0 ]; do
|
|||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [ "$UART_LOGGING" -eq 1 ] && [ "$LDR_SET" -eq 0 ]; then
|
||||||
|
LDR_MAKE="nx_audit"
|
||||||
|
fi
|
||||||
|
|
||||||
LDR_BUILD_PATH="${LDR_MAKE#nx_}"
|
LDR_BUILD_PATH="${LDR_MAKE#nx_}"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
@@ -37,8 +55,24 @@ if [ "$NO_EXO" -eq 1 ]; then
|
|||||||
echo "NO_EXO = 1"
|
echo "NO_EXO = 1"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$UART_LOGGING" -eq 1 ]; then
|
||||||
|
echo "UART_LOGGING = 1 (loader build: $LDR_MAKE, UART logging enabled)"
|
||||||
|
fi
|
||||||
|
|
||||||
CORES="$(nproc --all)"
|
CORES="$(nproc --all)"
|
||||||
echo "CORES: $CORES"
|
echo "CORES: $CORES"
|
||||||
|
JOBS="${JOBS:-$CORES}"
|
||||||
|
echo "JOBS: $JOBS"
|
||||||
|
|
||||||
|
if command -v ccache >/dev/null 2>&1; then
|
||||||
|
export ATMOSPHERE_CCACHE="ccache"
|
||||||
|
export CCACHE_BASEDIR="$ROOT_DIR"
|
||||||
|
export CCACHE_SLOPPINESS="time_macros,include_file_mtime,include_file_ctime,pch_defines,locale"
|
||||||
|
export CCACHE_MAXSIZE="10G"
|
||||||
|
echo "CCACHE: enabled ($(ccache --version | head -n1))"
|
||||||
|
else
|
||||||
|
echo "CCACHE: not found, building without it"
|
||||||
|
fi
|
||||||
|
|
||||||
SRC="Source/Atmosphere/stratosphere/loader/"
|
SRC="Source/Atmosphere/stratosphere/loader/"
|
||||||
|
|
||||||
@@ -61,29 +95,49 @@ mkdir -p "$DEST"
|
|||||||
echo
|
echo
|
||||||
echo "*** Patching loader ***"
|
echo "*** Patching loader ***"
|
||||||
cp -vr "$SRC"/. "$DEST"/
|
cp -vr "$SRC"/. "$DEST"/
|
||||||
echo
|
|
||||||
|
|
||||||
if [ "$NO_EXO" -eq 0 ]; then
|
if [ "$NO_EXO" -eq 0 ]; then
|
||||||
|
echo
|
||||||
echo "*** Patching exosphere ***"
|
echo "*** Patching exosphere ***"
|
||||||
EXO_SRC="Source/Atmosphere-Patches"
|
EXO_SRC="Source/Atmosphere-Patches"
|
||||||
EXO_DEST="build/atmosphere/exosphere/program/source/smc"
|
EXO_DEST="build/atmosphere/exosphere/program/source/smc"
|
||||||
LIBEXO_DEST="build/atmosphere/libraries/libexosphere/include/exosphere/secmon"
|
LIBEXO_DEST="build/atmosphere/libraries/libexosphere/include/exosphere/secmon"
|
||||||
|
|
||||||
cp -v "$EXO_SRC/secmon_define_emc_access_table.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_emc_access_table_data.inc" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_emc_access_table_data.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_define_emc_access_table.inc" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_define_emc01_access_table.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_rtc_pmc_access_table_data.inc" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_emc01_access_table_data.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_define_rtc_pmc_access_table.inc" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_define_rtc_pmc_access_table.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_smc_register_access.cpp" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_rtc_pmc_access_table_data.inc" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_smc_handler.cpp" "$EXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_smc_register_access.cpp" "$EXO_DEST/"
|
cp -v "$EXO_SRC/secmon_memory_layout.hpp" "$LIBEXO_DEST/"
|
||||||
cp -v "$EXO_SRC/secmon_smc_handler.cpp" "$EXO_DEST/"
|
|
||||||
cp -v "$EXO_SRC/secmon_memory_layout.hpp" "$LIBEXO_DEST/"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
CCACHE_MKS="build/atmosphere/libraries/config/common.mk
|
||||||
|
build/atmosphere/libraries/config/templates/stratosphere.mk
|
||||||
|
build/atmosphere/libraries/libstratosphere/libstratosphere.mk"
|
||||||
|
|
||||||
|
for CCACHE_MK in $CCACHE_MKS; do
|
||||||
|
if ! grep -q "ATMOSPHERE_CCACHE" "$CCACHE_MK"; then
|
||||||
|
echo
|
||||||
|
echo "*** Patching $CCACHE_MK for ccache ***"
|
||||||
|
cat >> "$CCACHE_MK" <<'EOF'
|
||||||
|
|
||||||
|
ifneq ($(strip $(ATMOSPHERE_CCACHE)),)
|
||||||
|
ifneq ($(firstword $(CC)),$(ATMOSPHERE_CCACHE))
|
||||||
|
export CC := $(ATMOSPHERE_CCACHE) $(CC)
|
||||||
|
endif
|
||||||
|
ifneq ($(firstword $(CXX)),$(ATMOSPHERE_CCACHE))
|
||||||
|
export CXX := $(ATMOSPHERE_CCACHE) $(CXX)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "*** Compiling loader ***"
|
echo "*** Compiling loader ***"
|
||||||
cd build/atmosphere/stratosphere/loader || exit 1
|
cd build/atmosphere/stratosphere/loader || exit 1
|
||||||
make -j$CORES "$LDR_MAKE"
|
make -j$JOBS HOC_UART_LOG=$UART_LOGGING "$LDR_MAKE"
|
||||||
hactool -t kip1 "out/nintendo_nx_arm64_armv8a/$LDR_BUILD_PATH/loader.kip" --uncompress=hoc.kip
|
hactool -t kip1 "out/nintendo_nx_arm64_armv8a/$LDR_BUILD_PATH/loader.kip" --uncompress=hoc.kip
|
||||||
cd "$ROOT_DIR" # exit
|
cd "$ROOT_DIR" # exit
|
||||||
cp -v build/atmosphere/stratosphere/loader/hoc.kip dist/atmosphere/kips/hoc.kip
|
cp -v build/atmosphere/stratosphere/loader/hoc.kip dist/atmosphere/kips/hoc.kip
|
||||||
@@ -92,11 +146,17 @@ if [ "$NO_EXO" -eq 0 ]; then
|
|||||||
echo
|
echo
|
||||||
echo "*** Compiling exosphere ***"
|
echo "*** Compiling exosphere ***"
|
||||||
cd build/atmosphere/exosphere
|
cd build/atmosphere/exosphere
|
||||||
make -j$CORES
|
make -j$JOBS
|
||||||
cd "$ROOT_DIR"
|
cd "$ROOT_DIR"
|
||||||
cp -v build/atmosphere/exosphere/out/nintendo_nx_arm64_armv8a/release/exosphere.bin dist/atmosphere/exosphere.bin
|
cp -v build/atmosphere/exosphere/out/nintendo_nx_arm64_armv8a/release/exosphere.bin dist/atmosphere/exosphere.bin
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$ATMOSPHERE_CCACHE" ]; then
|
||||||
|
echo
|
||||||
|
echo "*** ccache stats ***"
|
||||||
|
ccache --show-stats
|
||||||
|
fi
|
||||||
|
|
||||||
cd Source/hoc-clk/
|
cd Source/hoc-clk/
|
||||||
./build.sh
|
./build.sh
|
||||||
cp -r dist/ ../../
|
cp -r dist/ ../../
|
||||||
@@ -126,7 +186,7 @@ if [ "$EXT" -eq 1 ]; then
|
|||||||
cd hekate/
|
cd hekate/
|
||||||
echo
|
echo
|
||||||
echo "*** Compiling custom Hekate ***"
|
echo "*** Compiling custom Hekate ***"
|
||||||
make -j$CORES
|
make -j$JOBS
|
||||||
echo
|
echo
|
||||||
|
|
||||||
mkdir -p "$DIST_DIR/bootloader/sys/"
|
mkdir -p "$DIST_DIR/bootloader/sys/"
|
||||||
@@ -137,7 +197,7 @@ if [ "$EXT" -eq 1 ]; then
|
|||||||
cd "$ROOT_DIR"/Source/Benchmark-Toolbox
|
cd "$ROOT_DIR"/Source/Benchmark-Toolbox
|
||||||
echo
|
echo
|
||||||
echo "*** Compiling Benchmark-Toolbox ***"
|
echo "*** Compiling Benchmark-Toolbox ***"
|
||||||
make -j$CORES
|
make -j$JOBS
|
||||||
cp -v Benchmark-Toolbox.nro "$DIST_DIR"/switch/Benchmark-Toolbox.nro
|
cp -v Benchmark-Toolbox.nro "$DIST_DIR"/switch/Benchmark-Toolbox.nro
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user