From dbbf26f03c5d313aa5399074c680ded502977a90 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 17 Sep 2020 21:51:59 -0700 Subject: [PATCH] ams: expose reboot payload for kernel panic --- .../nintendo/nx/kern_k_system_control.cpp | 29 +++++++++- .../board/nintendo/nx/kern_secure_monitor.cpp | 57 +++++++------------ .../board/nintendo/nx/kern_secure_monitor.hpp | 10 +++- .../include/stratosphere/spl/smc/spl_smc.hpp | 8 ++- .../include/stratosphere/spl/spl_types.hpp | 2 + libstratosphere/source/spl/smc/spl_smc.cpp | 4 +- 6 files changed, 63 insertions(+), 47 deletions(-) diff --git a/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 8b717009..0441af3f 100644 --- a/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -534,10 +534,14 @@ namespace ams::kern::board::nintendo::nx { void KSystemControl::StopSystem(void *arg) { if (arg != nullptr) { + /* Get the address of the legacy IRAM region. */ + const KVirtualAddress iram_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsIram) + 64_KB; + constexpr size_t RebootPayloadSize = 0x2E000; + /* NOTE: Atmosphere extension; if we received an exception context from Panic(), */ /* generate a fatal error report using it. */ const KExceptionContext *e_ctx = static_cast(arg); - auto *f_ctx = GetPointer<::ams::impl::FatalErrorContext>(KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsIram) + 0x3E000); + auto *f_ctx = GetPointer<::ams::impl::FatalErrorContext>(iram_address + RebootPayloadSize); /* Clear the fatal context. */ std::memset(f_ctx, 0xCC, sizeof(*f_ctx)); @@ -581,8 +585,27 @@ namespace ams::kern::board::nintendo::nx { } } - /* Trigger a reboot to rcm. */ - smc::init::SetConfig(smc::ConfigItem::ExosphereNeedsReboot, smc::UserRebootType_ToRcm); + /* Try to get a payload address. */ + const KMemoryRegion *cached_region = nullptr; + u64 reboot_payload_paddr = 0; + if (smc::TryGetConfig(std::addressof(reboot_payload_paddr), 1, smc::ConfigItem::ExospherePayloadAddress) && KMemoryLayout::IsLinearMappedPhysicalAddress(cached_region, reboot_payload_paddr, RebootPayloadSize)) { + /* If we have a payload, reboot to it. */ + const KVirtualAddress reboot_payload = KMemoryLayout::GetLinearVirtualAddress(KPhysicalAddress(reboot_payload_paddr)); + + /* Clear IRAM. */ + std::memset(GetVoidPointer(iram_address), 0xCC, RebootPayloadSize); + + /* Copy the payload to iram. */ + for (size_t i = 0; i < RebootPayloadSize / sizeof(u32); ++i) { + GetPointer(iram_address)[i] = GetPointer(reboot_payload)[i]; + } + + /* Reboot. */ + smc::SetConfig(smc::ConfigItem::ExosphereNeedsReboot, smc::UserRebootType_ToPayload); + } else { + /* If we don't have a payload, reboot to rcm. */ + smc::SetConfig(smc::ConfigItem::ExosphereNeedsReboot, smc::UserRebootType_ToRcm); + } } if (g_call_smc_on_panic) { diff --git a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp index 315f5072..76af4e48 100644 --- a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp +++ b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp @@ -37,6 +37,9 @@ namespace ams::kern::board::nintendo::nx::smc { FunctionId_Panic = 0xC3000006, FunctionId_ConfigureCarveout = 0xC3000007, FunctionId_ReadWriteRegister = 0xC3000008, + + /* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */ + FunctionId_SetConfig = 0xC3000409, }; void CallPrivilegedSecureMonitorFunction(SecureMonitorArguments &args) { @@ -140,35 +143,6 @@ namespace ams::kern::board::nintendo::nx::smc { args.x[7] = x7; } - void CallUserSecureMonitorFunctionForInit(SecureMonitorArguments &args) { - /* Load arguments into registers. */ - register u64 x0 asm("x0") = args.x[0]; - register u64 x1 asm("x1") = args.x[1]; - register u64 x2 asm("x2") = args.x[2]; - register u64 x3 asm("x3") = args.x[3]; - register u64 x4 asm("x4") = args.x[4]; - register u64 x5 asm("x5") = args.x[5]; - register u64 x6 asm("x6") = args.x[6]; - register u64 x7 asm("x7") = args.x[7]; - - /* Actually make the call. */ - __asm__ __volatile__("smc #0" - : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7) - : - : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory" - ); - - /* Store arguments to output. */ - args.x[0] = x0; - args.x[1] = x1; - args.x[2] = x2; - args.x[3] = x3; - args.x[4] = x4; - args.x[5] = x5; - args.x[6] = x6; - args.x[7] = x7; - } - /* Global lock for generate random bytes. */ KSpinLock g_generate_random_lock; @@ -210,21 +184,30 @@ namespace ams::kern::board::nintendo::nx::smc { return static_cast(args.x[0]) == SmcResult::Success; } - bool SetConfig(ConfigItem config_item, u64 value) { - SecureMonitorArguments args = { UserFunctionId_SetConfig, static_cast(config_item), 0, value }; - CallUserSecureMonitorFunctionForInit(args); - return static_cast(args.x[0]) == SmcResult::Success; - } - } - void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { + bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { SecureMonitorArguments args = { FunctionId_GetConfig, static_cast(config_item) }; CallPrivilegedSecureMonitorFunction(args); - MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + if (static_cast(args.x[0]) != SmcResult::Success) { + return false; + } + for (size_t i = 0; i < num_qwords && i < 7; i++) { out[i] = args.x[1 + i]; } + + return true; + } + + void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { + MESOSPHERE_ABORT_UNLESS(TryGetConfig(out, num_qwords, config_item)); + } + + bool SetConfig(ConfigItem config_item, u64 value) { + SecureMonitorArguments args = { FunctionId_SetConfig, static_cast(config_item), 0, value }; + CallPrivilegedSecureMonitorFunction(args); + return static_cast(args.x[0]) == SmcResult::Success; } bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) { diff --git a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp index da63d29e..b7d17072 100644 --- a/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp +++ b/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp @@ -60,6 +60,10 @@ namespace ams::kern::board::nintendo::nx::smc { ExosphereNeedsShutdown = 65002, ExosphereGitCommitHash = 65003, ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, }; enum class SmcResult { @@ -89,12 +93,14 @@ namespace ams::kern::board::nintendo::nx::smc { UserRebootType_ToPayload = 2, }; - /* TODO: Rest of Secure Monitor API. */ void GenerateRandomBytes(void *dst, size_t size); + bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); void ConfigureCarveout(size_t which, uintptr_t address, size_t size); + bool SetConfig(ConfigItem config_item, u64 value); + void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg); void NORETURN Panic(u32 color); @@ -108,8 +114,6 @@ namespace ams::kern::board::nintendo::nx::smc { void GenerateRandomBytes(void *dst, size_t size); bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value); - bool SetConfig(ConfigItem config_item, u64 value); - } } \ No newline at end of file diff --git a/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp b/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp index 38a98d5d..ace91218 100644 --- a/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp +++ b/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp @@ -29,7 +29,7 @@ namespace ams::spl::smc { } /* Functions. */ - Result SetConfig(spl::ConfigItem which, const u64 *value, size_t num_qwords); + Result SetConfig(spl::ConfigItem which, const void *address, const u64 *value, size_t num_qwords); Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem which); Result GetResult(Result *out, AsyncOperationKey op); Result GetResultData(Result *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op); @@ -59,8 +59,12 @@ namespace ams::spl::smc { Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id); /* Helpers. */ + inline Result SetConfig(spl::ConfigItem which, const u64 *value, size_t num_qwords) { + return SetConfig(which, nullptr, value, num_qwords); + } + inline Result SetConfig(spl::ConfigItem which, const u64 value) { - return SetConfig(which, &value, 1); + return SetConfig(which, std::addressof(value), 1); } } diff --git a/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libstratosphere/include/stratosphere/spl/spl_types.hpp index 310fcebc..6232c3ee 100644 --- a/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libstratosphere/include/stratosphere/spl/spl_types.hpp @@ -222,6 +222,7 @@ namespace ams::spl { ExosphereBlankProdInfo = 65005, ExosphereAllowCalWrites = 65006, ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, }; } @@ -235,3 +236,4 @@ constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_ca constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast(65005); constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast(65006); constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast(65007); +constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast(65008); diff --git a/libstratosphere/source/spl/smc/spl_smc.cpp b/libstratosphere/source/spl/smc/spl_smc.cpp index 02934c7a..affed9de 100644 --- a/libstratosphere/source/spl/smc/spl_smc.cpp +++ b/libstratosphere/source/spl/smc/spl_smc.cpp @@ -17,12 +17,12 @@ namespace ams::spl::smc { - Result SetConfig(spl::ConfigItem which, const u64 *value, size_t num_qwords) { + Result SetConfig(spl::ConfigItem which, const void *address, const u64 *value, size_t num_qwords) { SecmonArgs args; args.X[0] = static_cast(FunctionId::SetConfig); args.X[1] = static_cast(which); - args.X[2] = 0; + args.X[2] = reinterpret_cast(address); for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) { args.X[3 + i] = value[i]; }