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

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

View file

@ -18,6 +18,7 @@
namespace ams::secmon {
bool IsPhysicalMemoryAddress(uintptr_t address);
size_t GetPhysicalMemorySize();
void UnmapTzram();

View file

@ -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[] = {

View file

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

View file

@ -48,6 +48,7 @@ namespace ams::secmon::smc {
ExosphereBlankProdInfo = 65005,
ExosphereAllowCalWrites = 65006,
ExosphereEmummcType = 65007,
ExospherePayloadAddress = 65008,
};
SmcResult SmcGetConfigUser(SmcArguments &args);

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

View file

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

View file

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

View file

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

View file

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