ams: expose reboot payload for kernel panic

This commit is contained in:
Michael Scire 2020-09-17 21:51:59 -07:00
parent 8d46d901d9
commit 48b4dd48a4
12 changed files with 97 additions and 51 deletions

View file

@ -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) {

View file

@ -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) {

View file

@ -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);
}
}