mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
ams: expose reboot payload for kernel panic
This commit is contained in:
parent
8d46d901d9
commit
48b4dd48a4
12 changed files with 97 additions and 51 deletions
|
@ -96,10 +96,6 @@ namespace ams::secmon {
|
|||
util::ClearMemory(reinterpret_cast<void *>(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<u64>();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
namespace ams::secmon {
|
||||
|
||||
bool IsPhysicalMemoryAddress(uintptr_t address);
|
||||
size_t GetPhysicalMemorySize();
|
||||
|
||||
void UnmapTzram();
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
#include <exosphere.hpp>
|
||||
#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<ConfigItem>(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;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace ams::secmon::smc {
|
|||
ExosphereBlankProdInfo = 65005,
|
||||
ExosphereAllowCalWrites = 65006,
|
||||
ExosphereEmummcType = 65007,
|
||||
ExospherePayloadAddress = 65008,
|
||||
};
|
||||
|
||||
SmcResult SmcGetConfigUser(SmcArguments &args);
|
||||
|
|
|
@ -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<const KExceptionContext *>(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<volatile u32>(iram_address)[i] = GetPointer<volatile u32>(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) {
|
||||
|
|
|
@ -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<SmcResult>(args.x[0]) == SmcResult::Success;
|
||||
}
|
||||
|
||||
bool SetConfig(ConfigItem config_item, u64 value) {
|
||||
SecureMonitorArguments args = { UserFunctionId_SetConfig, static_cast<u32>(config_item), 0, value };
|
||||
CallUserSecureMonitorFunctionForInit(args);
|
||||
return static_cast<SmcResult>(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<u32>(config_item) };
|
||||
CallPrivilegedSecureMonitorFunction(args);
|
||||
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
||||
if (static_cast<SmcResult>(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<u32>(config_item), 0, value };
|
||||
CallPrivilegedSecureMonitorFunction(args);
|
||||
return static_cast<SmcResult>(args.x[0]) == SmcResult::Success;
|
||||
}
|
||||
|
||||
bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<SplConfigItem>(65005);
|
||||
constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast<SplConfigItem>(65006);
|
||||
constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast<SplConfigItem>(65007);
|
||||
constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast<SplConfigItem>(65008);
|
||||
|
|
|
@ -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<u64>(FunctionId::SetConfig);
|
||||
args.X[1] = static_cast<u64>(which);
|
||||
args.X[2] = 0;
|
||||
args.X[2] = reinterpret_cast<u64>(address);
|
||||
for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) {
|
||||
args.X[3 + i] = value[i];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue