kern: switch->nx, implement sleep manager init
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
#include "kern_k_sleep_manager.hpp"
|
||||
#include "kern_secure_monitor.hpp"
|
||||
|
||||
namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Struct representing registers saved on wake/sleep. */
|
||||
class SavedSystemRegisters {
|
||||
private:
|
||||
u64 ttbr0_el1;
|
||||
u64 tcr_el1;
|
||||
u64 elr_el1;
|
||||
u64 sp_el0;
|
||||
u64 spsr_el1;
|
||||
u64 daif;
|
||||
u64 cpacr_el1;
|
||||
u64 vbar_el1;
|
||||
u64 csselr_el1;
|
||||
u64 cntp_ctl_el0;
|
||||
u64 cntp_cval_el0;
|
||||
u64 cntkctl_el1;
|
||||
u64 tpidr_el0;
|
||||
u64 tpidrro_el0;
|
||||
u64 mdscr_el1;
|
||||
u64 contextidr_el1;
|
||||
u64 dbgwcrN_el1[16];
|
||||
u64 dbgwvrN_el1[16];
|
||||
u64 dbgbcrN_el1[16];
|
||||
u64 dbgbvrN_el1[16];
|
||||
u64 pmccfiltr_el0;
|
||||
u64 pmccntr_el0;
|
||||
u64 pmcntenset_el0;
|
||||
u64 pmcr_el0;
|
||||
u64 pmevcntrN_el0[31];
|
||||
u64 pmevtyperN_el0[31];
|
||||
u64 pmcntenset_el1;
|
||||
u64 pmovsset_el0;
|
||||
u64 pmselr_el0;
|
||||
u64 pmuserenr_el0;
|
||||
public:
|
||||
void Save();
|
||||
void Restore() const;
|
||||
};
|
||||
|
||||
constexpr s32 SleepManagerThreadPriority = 2;
|
||||
|
||||
/* Globals for sleep/wake. */
|
||||
u64 g_sleep_target_cores;
|
||||
KLightLock g_request_lock;
|
||||
KLightLock g_cv_lock;
|
||||
KLightConditionVariable g_cv;
|
||||
KPhysicalAddress g_sleep_buffer_phys_addrs[cpu::NumCores];
|
||||
alignas(16) u64 g_sleep_buffers[cpu::NumCores][1_KB / sizeof(u64)];
|
||||
SavedSystemRegisters g_sleep_system_registers[cpu::NumCores] = {};
|
||||
|
||||
}
|
||||
|
||||
void KSleepManager::Initialize() {
|
||||
/* Create a sleep manager thread for each core. */
|
||||
for (size_t core_id = 0; core_id < cpu::NumCores; core_id++) {
|
||||
/* Reserve a thread from the system limit. */
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_ThreadCountMax, 1));
|
||||
|
||||
/* Create a new thread. */
|
||||
KThread *new_thread = KThread::Create();
|
||||
MESOSPHERE_ABORT_UNLESS(new_thread != nullptr);
|
||||
|
||||
/* Launch the new thread. */
|
||||
MESOSPHERE_R_ABORT_UNLESS(KThread::InitializeKernelThread(new_thread, KSleepManager::ProcessRequests, reinterpret_cast<uintptr_t>(g_sleep_buffers[core_id]), SleepManagerThreadPriority, static_cast<s32>(core_id)));
|
||||
|
||||
/* Register the new thread. */
|
||||
KThread::Register(new_thread);
|
||||
|
||||
/* Run the thread. */
|
||||
new_thread->Run();
|
||||
}
|
||||
}
|
||||
|
||||
void KSleepManager::SleepSystem() {
|
||||
/* Ensure device mappings are not modified during sleep. */
|
||||
MESOSPHERE_TODO("KDevicePageTable::Lock();");
|
||||
ON_SCOPE_EXIT { MESOSPHERE_TODO("KDevicePageTable::Unlock();"); };
|
||||
|
||||
/* Request that the system sleep. */
|
||||
{
|
||||
KScopedLightLock lk(g_request_lock);
|
||||
|
||||
/* Signal the manager to sleep on all cores. */
|
||||
{
|
||||
KScopedLightLock lk(g_cv_lock);
|
||||
MESOSPHERE_ABORT_UNLESS(g_sleep_target_cores == 0);
|
||||
|
||||
g_sleep_target_cores = (1ul << (cpu::NumCores - 1));
|
||||
g_cv.Broadcast();
|
||||
|
||||
while (g_sleep_target_cores != 0) {
|
||||
g_cv.Wait(std::addressof(g_cv_lock));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KSleepManager::ProcessRequests(uintptr_t buffer) {
|
||||
const s32 core_id = GetCurrentCoreId();
|
||||
KPhysicalAddress resume_entry_phys_addr = Null<KPhysicalAddress>;
|
||||
|
||||
/* Get the physical addresses we'll need. */
|
||||
{
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetKernelPageTable().GetPhysicalAddress(std::addressof(g_sleep_buffer_phys_addrs[core_id]), KProcessAddress(buffer)));
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetKernelPageTable().GetPhysicalAddress(std::addressof(resume_entry_phys_addr), KProcessAddress(&::ams::kern::board::nintendo::nx::KSleepManager::ResumeEntry)));
|
||||
|
||||
}
|
||||
const KPhysicalAddress sleep_buffer_phys_addr = g_sleep_buffer_phys_addrs[core_id];
|
||||
const u64 target_core_mask = (1ul << core_id);
|
||||
|
||||
/* Loop, processing sleep when requested. */
|
||||
while (true) {
|
||||
/* Wait for a request. */
|
||||
{
|
||||
KScopedLightLock lk(g_cv_lock);
|
||||
while (!(g_sleep_target_cores & target_core_mask)) {
|
||||
g_cv.Wait(std::addressof(g_cv_lock));
|
||||
}
|
||||
}
|
||||
|
||||
MESOSPHERE_TODO("Implement Sleep/Wake");
|
||||
(void)(g_sleep_system_registers[core_id]);
|
||||
(void)(sleep_buffer_phys_addr);
|
||||
|
||||
/* Signal request completed. */
|
||||
{
|
||||
KScopedLightLock lk(g_cv_lock);
|
||||
g_sleep_target_cores &= ~target_core_mask;
|
||||
if (g_sleep_target_cores == 0) {
|
||||
g_cv.Broadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
class KSleepManager {
|
||||
private:
|
||||
static void CpuSleepHandler(uintptr_t arg, uintptr_t entry);
|
||||
static void ResumeEntry(uintptr_t arg);
|
||||
|
||||
static void ProcessRequests(uintptr_t buffer);
|
||||
public:
|
||||
static void Initialize();
|
||||
static void SleepSystem();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* ams::kern::board::nintendo::nx::KSleepManager::ResumeEntry(uintptr_t arg) */
|
||||
.section .text._ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm, "ax", %progbits
|
||||
.global _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm
|
||||
.type _ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm, %function
|
||||
_ZN3ams4kern5board8nintendo2nx13KSleepManager11ResumeEntryEm:
|
||||
/* TODO: Implement a real function here. */
|
||||
brk 1000
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
#include "kern_secure_monitor.hpp"
|
||||
#include "kern_k_sleep_manager.hpp"
|
||||
|
||||
namespace ams::kern::board::nintendo::nx {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Global variables for panic. */
|
||||
bool g_call_smc_on_panic;
|
||||
|
||||
/* Global variables for secure memory. */
|
||||
constexpr size_t SecureAppletReservedMemorySize = 4_MB;
|
||||
KVirtualAddress g_secure_applet_memory_address;
|
||||
|
||||
|
||||
/* Global variables for randomness. */
|
||||
/* Nintendo uses std::mt19937_t for randomness. */
|
||||
/* To save space (and because mt19337_t isn't secure anyway), */
|
||||
/* We will use TinyMT. */
|
||||
bool g_initialized_random_generator;
|
||||
util::TinyMT g_random_generator;
|
||||
KSpinLock g_random_lock;
|
||||
|
||||
ALWAYS_INLINE size_t GetRealMemorySizeForInit() {
|
||||
/* TODO: Move this into a header for the MC in general. */
|
||||
constexpr u32 MemoryControllerConfigurationRegister = 0x70019050;
|
||||
u32 config_value;
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(smc::init::ReadWriteRegister(&config_value, MemoryControllerConfigurationRegister, 0, 0));
|
||||
return static_cast<size_t>(config_value & 0x3FFF) << 20;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE util::BitPack32 GetKernelConfigurationForInit() {
|
||||
u64 value = 0;
|
||||
smc::init::GetConfig(&value, 1, smc::ConfigItem::KernelConfiguration);
|
||||
return util::BitPack32{static_cast<u32>(value)};
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 GetMemoryModeForInit() {
|
||||
u64 value = 0;
|
||||
smc::init::GetConfig(&value, 1, smc::ConfigItem::MemoryMode);
|
||||
return static_cast<u32>(value);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE smc::MemoryArrangement GetMemoryArrangeForInit() {
|
||||
switch(GetMemoryModeForInit() & 0x3F) {
|
||||
case 0x01:
|
||||
default:
|
||||
return smc::MemoryArrangement_4GB;
|
||||
case 0x02:
|
||||
return smc::MemoryArrangement_4GBForAppletDev;
|
||||
case 0x03:
|
||||
return smc::MemoryArrangement_4GBForSystemDev;
|
||||
case 0x11:
|
||||
return smc::MemoryArrangement_6GB;
|
||||
case 0x12:
|
||||
return smc::MemoryArrangement_6GBForAppletDev;
|
||||
case 0x21:
|
||||
return smc::MemoryArrangement_8GB;
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u64 GenerateRandomU64ForInit() {
|
||||
u64 value;
|
||||
smc::init::GenerateRandomBytes(&value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u64 GenerateRandomU64FromGenerator() {
|
||||
return g_random_generator.GenerateRandomU64();
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
ALWAYS_INLINE u64 GenerateUniformRange(u64 min, u64 max, F f) {
|
||||
/* Handle the case where the difference is too large to represent. */
|
||||
if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) {
|
||||
return f();
|
||||
}
|
||||
|
||||
/* Iterate until we get a value in range. */
|
||||
const u64 range_size = ((max + 1) - min);
|
||||
const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size;
|
||||
while (true) {
|
||||
if (const u64 rnd = f(); rnd < effective_max) {
|
||||
return min + (rnd % range_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u64 GetConfigU64(smc::ConfigItem which) {
|
||||
u64 value;
|
||||
smc::GetConfig(&value, 1, which);
|
||||
return value;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 GetConfigU32(smc::ConfigItem which) {
|
||||
return static_cast<u32>(GetConfigU64(which));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool GetConfigBool(smc::ConfigItem which) {
|
||||
return GetConfigU64(which) != 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Initialization. */
|
||||
size_t KSystemControl::Init::GetIntendedMemorySize() {
|
||||
switch (GetKernelConfigurationForInit().Get<smc::KernelConfiguration::MemorySize>()) {
|
||||
case smc::MemorySize_4GB:
|
||||
default: /* All invalid modes should go to 4GB. */
|
||||
return 4_GB;
|
||||
case smc::MemorySize_6GB:
|
||||
return 6_GB;
|
||||
case smc::MemorySize_8GB:
|
||||
return 8_GB;
|
||||
}
|
||||
}
|
||||
|
||||
KPhysicalAddress KSystemControl::Init::GetKernelPhysicalBaseAddress(uintptr_t base_address) {
|
||||
const size_t real_dram_size = GetRealMemorySizeForInit();
|
||||
const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
|
||||
if (intended_dram_size * 2 < real_dram_size) {
|
||||
return base_address;
|
||||
} else {
|
||||
return base_address + ((real_dram_size - intended_dram_size) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
|
||||
return GetKernelConfigurationForInit().Get<smc::KernelConfiguration::IncreaseThreadResourceLimit>();
|
||||
}
|
||||
|
||||
size_t KSystemControl::Init::GetApplicationPoolSize() {
|
||||
switch (GetMemoryArrangeForInit()) {
|
||||
case smc::MemoryArrangement_4GB:
|
||||
default:
|
||||
return 3285_MB;
|
||||
case smc::MemoryArrangement_4GBForAppletDev:
|
||||
return 2048_MB;
|
||||
case smc::MemoryArrangement_4GBForSystemDev:
|
||||
return 3285_MB;
|
||||
case smc::MemoryArrangement_6GB:
|
||||
return 4916_MB;
|
||||
case smc::MemoryArrangement_6GBForAppletDev:
|
||||
return 3285_MB;
|
||||
case smc::MemoryArrangement_8GB:
|
||||
return 4916_MB;
|
||||
}
|
||||
}
|
||||
|
||||
size_t KSystemControl::Init::GetAppletPoolSize() {
|
||||
switch (GetMemoryArrangeForInit()) {
|
||||
case smc::MemoryArrangement_4GB:
|
||||
default:
|
||||
return 507_MB;
|
||||
case smc::MemoryArrangement_4GBForAppletDev:
|
||||
return 1554_MB;
|
||||
case smc::MemoryArrangement_4GBForSystemDev:
|
||||
return 448_MB;
|
||||
case smc::MemoryArrangement_6GB:
|
||||
return 562_MB;
|
||||
case smc::MemoryArrangement_6GBForAppletDev:
|
||||
return 2193_MB;
|
||||
case smc::MemoryArrangement_8GB:
|
||||
return 2193_MB;
|
||||
}
|
||||
}
|
||||
|
||||
size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() {
|
||||
/* TODO: Where does this constant actually come from? */
|
||||
return 0x29C8000;
|
||||
}
|
||||
|
||||
void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||
smc::init::CpuOn(core_id, entrypoint, arg);
|
||||
}
|
||||
|
||||
/* Randomness for Initialization. */
|
||||
void KSystemControl::Init::GenerateRandomBytes(void *dst, size_t size) {
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(size <= 0x38);
|
||||
smc::init::GenerateRandomBytes(dst, size);
|
||||
}
|
||||
|
||||
u64 KSystemControl::Init::GenerateRandomRange(u64 min, u64 max) {
|
||||
return GenerateUniformRange(min, max, GenerateRandomU64ForInit);
|
||||
}
|
||||
|
||||
/* System Initialization. */
|
||||
void KSystemControl::InitializePhase1() {
|
||||
/* Set IsDebugMode. */
|
||||
{
|
||||
KTargetSystem::SetIsDebugMode(GetConfigBool(smc::ConfigItem::IsDebugMode));
|
||||
|
||||
/* If debug mode, we want to initialize uart logging. */
|
||||
KTargetSystem::EnableDebugLogging(KTargetSystem::IsDebugMode());
|
||||
KDebugLog::Initialize();
|
||||
}
|
||||
|
||||
/* Set Kernel Configuration. */
|
||||
{
|
||||
const auto kernel_config = util::BitPack32{GetConfigU32(smc::ConfigItem::KernelConfiguration)};
|
||||
|
||||
KTargetSystem::EnableDebugMemoryFill(kernel_config.Get<smc::KernelConfiguration::DebugFillMemory>());
|
||||
KTargetSystem::EnableUserExceptionHandlers(kernel_config.Get<smc::KernelConfiguration::EnableUserExceptionHandlers>());
|
||||
KTargetSystem::EnableUserPmuAccess(kernel_config.Get<smc::KernelConfiguration::EnableUserPmuAccess>());
|
||||
|
||||
g_call_smc_on_panic = kernel_config.Get<smc::KernelConfiguration::UseSecureMonitorPanicCall>();
|
||||
}
|
||||
|
||||
/* Set Kernel Debugging. */
|
||||
{
|
||||
/* NOTE: This is used to restrict access to SvcKernelDebug/SvcChangeKernelTraceState. */
|
||||
/* Mesosphere may wish to not require this, as we'd ideally keep ProgramVerification enabled for userland. */
|
||||
KTargetSystem::EnableKernelDebugging(GetConfigBool(smc::ConfigItem::DisableProgramVerification));
|
||||
}
|
||||
|
||||
/* Configure the Kernel Carveout region. */
|
||||
{
|
||||
const auto carveout = KMemoryLayout::GetCarveoutRegionExtents();
|
||||
smc::ConfigureCarveout(0, carveout.GetAddress(), carveout.GetSize());
|
||||
}
|
||||
|
||||
/* System ResourceLimit initialization. */
|
||||
{
|
||||
/* Construct the resource limit object. */
|
||||
KResourceLimit &sys_res_limit = Kernel::GetSystemResourceLimit();
|
||||
KAutoObject::Create(std::addressof(sys_res_limit));
|
||||
sys_res_limit.Initialize();
|
||||
|
||||
/* Set the initial limits. */
|
||||
const auto [total_memory_size, kernel_memory_size] = KMemoryLayout::GetTotalAndKernelMemorySizes();
|
||||
const auto &slab_counts = init::GetSlabResourceCounts();
|
||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_PhysicalMemoryMax, total_memory_size));
|
||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_ThreadCountMax, slab_counts.num_KThread));
|
||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_EventCountMax, slab_counts.num_KEvent));
|
||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_TransferMemoryCountMax, slab_counts.num_KTransferMemory));
|
||||
MESOSPHERE_R_ABORT_UNLESS(sys_res_limit.SetLimitValue(ams::svc::LimitableResource_SessionCountMax, slab_counts.num_KSession));
|
||||
|
||||
/* Reserve system memory. */
|
||||
MESOSPHERE_ABORT_UNLESS(sys_res_limit.Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, kernel_memory_size));
|
||||
}
|
||||
}
|
||||
|
||||
void KSystemControl::InitializePhase2() {
|
||||
/* Initialize the sleep manager. */
|
||||
KSleepManager::Initialize();
|
||||
|
||||
/* Reserve secure applet memory. */
|
||||
{
|
||||
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address == Null<KVirtualAddress>);
|
||||
MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, SecureAppletReservedMemorySize));
|
||||
|
||||
constexpr auto SecureAppletAllocateOption = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront);
|
||||
g_secure_applet_memory_address = Kernel::GetMemoryManager().AllocateContinuous(SecureAppletReservedMemorySize / PageSize, 1, SecureAppletAllocateOption);
|
||||
MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address != Null<KVirtualAddress>);
|
||||
}
|
||||
}
|
||||
|
||||
u32 KSystemControl::GetInitialProcessBinaryPool() {
|
||||
return KMemoryManager::Pool_Application;
|
||||
}
|
||||
|
||||
/* Randomness. */
|
||||
void KSystemControl::GenerateRandomBytes(void *dst, size_t size) {
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(size <= 0x38);
|
||||
smc::GenerateRandomBytes(dst, size);
|
||||
}
|
||||
|
||||
u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) {
|
||||
KScopedInterruptDisable intr_disable;
|
||||
KScopedSpinLock lk(g_random_lock);
|
||||
|
||||
if (AMS_UNLIKELY(!g_initialized_random_generator)) {
|
||||
u64 seed;
|
||||
GenerateRandomBytes(&seed, sizeof(seed));
|
||||
g_random_generator.Initialize(reinterpret_cast<u32*>(&seed), sizeof(seed) / sizeof(u32));
|
||||
g_initialized_random_generator = true;
|
||||
}
|
||||
|
||||
return GenerateUniformRange(min, max, GenerateRandomU64FromGenerator);
|
||||
}
|
||||
|
||||
void KSystemControl::SleepSystem() {
|
||||
MESOSPHERE_LOG("SleepSystem() was called\n");
|
||||
KSleepManager::SleepSystem();
|
||||
}
|
||||
|
||||
void KSystemControl::StopSystem() {
|
||||
if (g_call_smc_on_panic) {
|
||||
/* Display a panic screen via secure monitor. */
|
||||
/* TODO: Enable in release: smc::Panic(0xF00); */
|
||||
}
|
||||
while (true) { /* ... */ }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
#include "kern_secure_monitor.hpp"
|
||||
|
||||
namespace ams::kern::board::nintendo::nx::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct SecureMonitorArguments {
|
||||
u64 x[8];
|
||||
};
|
||||
|
||||
enum FunctionId : u32 {
|
||||
FunctionId_CpuSuspend = 0xC4000001,
|
||||
FunctionId_CpuOff = 0x84000002,
|
||||
FunctionId_CpuOn = 0xC4000003,
|
||||
FunctionId_GetConfig = 0xC3000004,
|
||||
FunctionId_GenerateRandomBytes = 0xC3000005,
|
||||
FunctionId_Panic = 0xC3000006,
|
||||
FunctionId_ConfigureCarveout = 0xC3000007,
|
||||
FunctionId_ReadWriteRegister = 0xC3000008,
|
||||
};
|
||||
|
||||
void CallPrivilegedSecureMonitorFunction(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. */
|
||||
{
|
||||
/* Disable interrupts while making the call. */
|
||||
KScopedInterruptDisable intr_disable;
|
||||
__asm__ __volatile__("smc #1"
|
||||
: "+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"
|
||||
);
|
||||
|
||||
/* Restore the CoreLocalRegion into X18. */
|
||||
cpu::SetCoreLocalRegionAddress(cpu::GetTpidrEl1());
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
void CallPrivilegedSecureMonitorFunctionForInit(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 #1"
|
||||
: "+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;
|
||||
|
||||
}
|
||||
|
||||
/* SMC functionality needed for init. */
|
||||
namespace init {
|
||||
|
||||
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) {
|
||||
SecureMonitorArguments args = { FunctionId_CpuOn, core_id, entrypoint, arg };
|
||||
CallPrivilegedSecureMonitorFunctionForInit(args);
|
||||
}
|
||||
|
||||
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) {
|
||||
SecureMonitorArguments args = { FunctionId_GetConfig, static_cast<u32>(config_item) };
|
||||
CallPrivilegedSecureMonitorFunctionForInit(args);
|
||||
MESOSPHERE_INIT_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
||||
for (size_t i = 0; i < num_qwords && i < 7; i++) {
|
||||
out[i] = args.x[1 + i];
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateRandomBytes(void *dst, size_t size) {
|
||||
/* Call SmcGenerateRandomBytes() */
|
||||
SecureMonitorArguments args = { FunctionId_GenerateRandomBytes, size };
|
||||
MESOSPHERE_INIT_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.x[0]));
|
||||
|
||||
CallPrivilegedSecureMonitorFunctionForInit(args);
|
||||
MESOSPHERE_INIT_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
||||
|
||||
/* Copy output. */
|
||||
std::memcpy(dst, &args.x[1], size);
|
||||
}
|
||||
|
||||
bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value) {
|
||||
SecureMonitorArguments args = { FunctionId_ReadWriteRegister, address, mask, value };
|
||||
CallPrivilegedSecureMonitorFunctionForInit(args);
|
||||
*out = args.x[1];
|
||||
return static_cast<SmcResult>(args.x[0]) == SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GetConfig(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));
|
||||
for (size_t i = 0; i < num_qwords && i < 7; i++) {
|
||||
out[i] = args.x[1 + i];
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureCarveout(size_t which, uintptr_t address, size_t size) {
|
||||
SecureMonitorArguments args = { FunctionId_ConfigureCarveout, static_cast<u64>(which), static_cast<u64>(address), static_cast<u64>(size) };
|
||||
CallPrivilegedSecureMonitorFunction(args);
|
||||
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
||||
}
|
||||
|
||||
void GenerateRandomBytes(void *dst, size_t size) {
|
||||
/* Setup for call. */
|
||||
SecureMonitorArguments args = { FunctionId_GenerateRandomBytes, size };
|
||||
MESOSPHERE_ABORT_UNLESS(size <= sizeof(args) - sizeof(args.x[0]));
|
||||
|
||||
/* Make call. */
|
||||
{
|
||||
KScopedInterruptDisable intr_disable;
|
||||
KScopedSpinLock lk(g_generate_random_lock);
|
||||
CallPrivilegedSecureMonitorFunction(args);
|
||||
}
|
||||
MESOSPHERE_ABORT_UNLESS((static_cast<SmcResult>(args.x[0]) == SmcResult::Success));
|
||||
|
||||
/* Copy output. */
|
||||
std::memcpy(dst, &args.x[1], size);
|
||||
}
|
||||
|
||||
void NORETURN Panic(u32 color) {
|
||||
SecureMonitorArguments args = { FunctionId_Panic, color };
|
||||
CallPrivilegedSecureMonitorFunction(args);
|
||||
while (true) { /* ... */ }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <mesosphere.hpp>
|
||||
|
||||
namespace ams::kern::board::nintendo::nx::smc {
|
||||
|
||||
/* Types. */
|
||||
enum MemorySize {
|
||||
MemorySize_4GB = 0,
|
||||
MemorySize_6GB = 1,
|
||||
MemorySize_8GB = 2,
|
||||
};
|
||||
|
||||
enum MemoryArrangement {
|
||||
MemoryArrangement_4GB = 0,
|
||||
MemoryArrangement_4GBForAppletDev = 1,
|
||||
MemoryArrangement_4GBForSystemDev = 2,
|
||||
MemoryArrangement_6GB = 3,
|
||||
MemoryArrangement_6GBForAppletDev = 4,
|
||||
MemoryArrangement_8GB = 5,
|
||||
};
|
||||
|
||||
enum class ConfigItem : u32 {
|
||||
/* Standard config items. */
|
||||
DisableProgramVerification = 1,
|
||||
DramId = 2,
|
||||
SecurityEngineIrqNumber = 3,
|
||||
Version = 4,
|
||||
HardwareType = 5,
|
||||
IsRetail = 6,
|
||||
IsRecoveryBoot = 7,
|
||||
DeviceId = 8,
|
||||
BootReason = 9,
|
||||
MemoryMode = 10,
|
||||
IsDebugMode = 11,
|
||||
KernelConfiguration = 12,
|
||||
IsChargerHiZModeEnabled = 13,
|
||||
IsQuest = 14,
|
||||
RegulatorType = 15,
|
||||
DeviceUniqueKeyGeneration = 16,
|
||||
Package2Hash = 17,
|
||||
|
||||
/* Extension config items for exosphere. */
|
||||
ExosphereApiVersion = 65000,
|
||||
ExosphereNeedsReboot = 65001,
|
||||
ExosphereNeedsShutdown = 65002,
|
||||
ExosphereGitCommitHash = 65003,
|
||||
ExosphereHasRcmBugPatch = 65004,
|
||||
};
|
||||
|
||||
enum class SmcResult {
|
||||
Success = 0,
|
||||
NotImplemented = 1,
|
||||
InvalidArgument = 2,
|
||||
InProgress = 3,
|
||||
NoAsyncOperation = 4,
|
||||
InvalidAsyncOperation = 5,
|
||||
NotPermitted = 6,
|
||||
};
|
||||
|
||||
struct KernelConfiguration {
|
||||
using DebugFillMemory = util::BitPack32::Field<0, 1, bool>;
|
||||
using EnableUserExceptionHandlers = util::BitPack32::Field<DebugFillMemory::Next, 1, bool>;
|
||||
using EnableUserPmuAccess = util::BitPack32::Field<EnableUserExceptionHandlers::Next, 1, bool>;
|
||||
using IncreaseThreadResourceLimit = util::BitPack32::Field<EnableUserPmuAccess::Next, 1, bool>;
|
||||
using Reserved4 = util::BitPack32::Field<IncreaseThreadResourceLimit::Next, 4, u32>;
|
||||
using UseSecureMonitorPanicCall = util::BitPack32::Field<Reserved4::Next, 1, bool>;
|
||||
using Reserved9 = util::BitPack32::Field<UseSecureMonitorPanicCall::Next, 7, u32>;
|
||||
using MemorySize = util::BitPack32::Field<Reserved9::Next, 2, smc::MemorySize>;
|
||||
};
|
||||
|
||||
/* TODO: Rest of Secure Monitor API. */
|
||||
void GenerateRandomBytes(void *dst, size_t size);
|
||||
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item);
|
||||
void ConfigureCarveout(size_t which, uintptr_t address, size_t size);
|
||||
void NORETURN Panic(u32 color);
|
||||
|
||||
namespace init {
|
||||
|
||||
void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg);
|
||||
void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item);
|
||||
void GenerateRandomBytes(void *dst, size_t size);
|
||||
bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user