From 48b4dd48a4bccb2ec12346081d28bfbae1dfa533 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 --- exosphere/program/source/secmon_map.cpp | 8 +-- exosphere/program/source/secmon_map.hpp | 1 + .../program/source/smc/secmon_smc_handler.cpp | 3 + .../program/source/smc/secmon_smc_info.cpp | 22 +++++++ .../program/source/smc/secmon_smc_info.hpp | 1 + .../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 + .../source/spl/smc/spl_smc.cpp | 4 +- .../source/bpc_mitm/bpc_ams_power_utils.cpp | 3 + 12 files changed, 97 insertions(+), 51 deletions(-) diff --git a/exosphere/program/source/secmon_map.cpp b/exosphere/program/source/secmon_map.cpp index 6c604fdaa..4dbe006f1 100644 --- a/exosphere/program/source/secmon_map.cpp +++ b/exosphere/program/source/secmon_map.cpp @@ -96,10 +96,6 @@ namespace ams::secmon { util::ClearMemory(reinterpret_cast(address + size / 2), size / 2); } - bool IsPhysicalMemoryAddress(uintptr_t address) { - return (address - MemoryRegionDram.GetAddress()) < GetPhysicalMemorySize(); - } - } void ClearBootCodeHigh() { @@ -130,6 +126,10 @@ namespace ams::secmon { } } + bool IsPhysicalMemoryAddress(uintptr_t address) { + return (address - MemoryRegionDram.GetAddress()) < GetPhysicalMemorySize(); + } + void UnmapTzram() { /* Get the tables. */ u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer(); diff --git a/exosphere/program/source/secmon_map.hpp b/exosphere/program/source/secmon_map.hpp index a6bdbc405..7cc0cbc56 100644 --- a/exosphere/program/source/secmon_map.hpp +++ b/exosphere/program/source/secmon_map.hpp @@ -18,6 +18,7 @@ namespace ams::secmon { + bool IsPhysicalMemoryAddress(uintptr_t address); size_t GetPhysicalMemorySize(); void UnmapTzram(); diff --git a/exosphere/program/source/smc/secmon_smc_handler.cpp b/exosphere/program/source/smc/secmon_smc_handler.cpp index 3c790e3a7..38d950ec8 100644 --- a/exosphere/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere/program/source/smc/secmon_smc_handler.cpp @@ -141,6 +141,9 @@ namespace ams::secmon::smc { { 0xC3000006, Restriction_Normal, SmcShowError }, { 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion }, { 0xC3000008, Restriction_Normal, SmcReadWriteRegister }, + + /* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */ + { 0xC3000409, Restriction_Normal, SmcSetConfig }, }; constinit HandlerInfo g_ams_handlers[] = { diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index d06b8a509..857eeccc4 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -15,6 +15,7 @@ */ #include #include "../secmon_error.hpp" +#include "../secmon_map.hpp" #include "../secmon_misc.hpp" #include "../secmon_page_mapper.hpp" #include "../secmon_user_power_management.hpp" @@ -157,6 +158,8 @@ namespace ams::secmon::smc { return value.value; } + constinit u64 g_payload_address = 0; + SmcResult GetConfig(SmcArguments &args, bool kern) { switch (static_cast(args.r[1])) { case ConfigItem::DisableProgramVerification: @@ -267,6 +270,14 @@ namespace ams::secmon::smc { /* NOTE: This may return values other than 1 in the future. */ args.r[1] = (GetEmummcConfiguration().IsEmummcActive() ? 1 : 0); break; + case ConfigItem::ExospherePayloadAddress: + /* Gets the physical address of the reboot payload buffer, if one exists. */ + if (g_payload_address != 0) { + args.r[1] = g_payload_address; + } else { + return SmcResult::NotInitialized; + } + break; default: return SmcResult::InvalidArgument; } @@ -309,6 +320,17 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } break; + case ConfigItem::ExospherePayloadAddress: + if (g_payload_address == 0) { + if (secmon::IsPhysicalMemoryAddress(args.r[2])) { + g_payload_address = args.r[2]; + } else { + return SmcResult::InvalidArgument; + } + } else { + return SmcResult::Busy; + } + break; default: return SmcResult::InvalidArgument; } diff --git a/exosphere/program/source/smc/secmon_smc_info.hpp b/exosphere/program/source/smc/secmon_smc_info.hpp index 55846631e..7f33cbdba 100644 --- a/exosphere/program/source/smc/secmon_smc_info.hpp +++ b/exosphere/program/source/smc/secmon_smc_info.hpp @@ -48,6 +48,7 @@ namespace ams::secmon::smc { ExosphereBlankProdInfo = 65005, ExosphereAllowCalWrites = 65006, ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, }; SmcResult SmcGetConfigUser(SmcArguments &args); diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 8b717009f..0441af3f3 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/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/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp index 315f5072e..76af4e484 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp +++ b/libraries/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/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp index da63d29e6..b7d17072e 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp b/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp index 38a98d5df..ace91218d 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp index 310fcebc9..6232c3ee0 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libraries/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/libraries/libstratosphere/source/spl/smc/spl_smc.cpp b/libraries/libstratosphere/source/spl/smc/spl_smc.cpp index 02934c7a9..affed9de4 100644 --- a/libraries/libstratosphere/source/spl/smc/spl_smc.cpp +++ b/libraries/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]; } diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp index 727cc61bc..d88b661b8 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp @@ -108,6 +108,9 @@ namespace ams::mitm::bpc { /* Copy in payload. */ std::memcpy(g_reboot_payload, payload, payload_size); + /* Note to the secure monitor that we have a payload. */ + spl::smc::SetConfig(spl::ConfigItem::ExospherePayloadAddress, g_reboot_payload, nullptr, 0); + /* NOTE: Preferred reboot type may be overrwritten when parsed from settings during boot. */ g_reboot_type = RebootType::ToPayload; }