From fd9b9869384c1cfef6d7c1ee00773ac4f64a3d0e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 24 Jul 2020 05:26:59 -0700 Subject: [PATCH] kern: Implement SecureMemory (system resource) --- .../include/mesosphere/kern_k_process.hpp | 18 +- .../nintendo/nx/kern_k_system_control.cpp | 168 +++++++++++++++++- .../source/svc/kern_svc_info.cpp | 23 ++- 3 files changed, 189 insertions(+), 20 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 53ba20db0..01c1e9af5 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -154,13 +154,11 @@ namespace ams::kern { constexpr KProcessAddress GetEntryPoint() const { return this->code_address; } constexpr u64 GetRandomEntropy(size_t i) const { return this->entropy[i]; } - constexpr bool IsSuspended() const { - return this->is_suspended; - } - constexpr void SetSuspended(bool suspended) { - this->is_suspended = suspended; - } + constexpr bool IsApplication() const { return this->is_application; } + + constexpr bool IsSuspended() const { return this->is_suspended; } + constexpr void SetSuspended(bool suspended) { this->is_suspended = suspended; } Result Terminate(); @@ -246,6 +244,14 @@ namespace ams::kern { void IncrementThreadCount(); void DecrementThreadCount(); + size_t GetTotalSystemResourceSize() const { return this->system_resource_num_pages * PageSize; } + size_t GetUsedSystemResourceSize() const { + if (this->system_resource_num_pages == 0) { + return 0; + } + return this->dynamic_page_manager.GetUsed() * PageSize; + } + void ClearRunningThread(KThread *thread) { for (size_t i = 0; i < util::size(this->running_threads); ++i) { if (this->running_threads[i] == thread) { 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 2fa8406b1..ca6e0f58c 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 @@ -21,13 +21,21 @@ namespace ams::kern::board::nintendo::nx { namespace { + constexpr size_t SecureAlignment = 128_KB; + /* Global variables for panic. */ - bool g_call_smc_on_panic; + constinit bool g_call_smc_on_panic; /* Global variables for secure memory. */ - constexpr size_t SecureAppletReservedMemorySize = 4_MB; - KVirtualAddress g_secure_applet_memory_address; + constexpr size_t SecureAppletMemorySize = 4_MB; + constinit KSpinLock g_secure_applet_lock; + constinit bool g_secure_applet_memory_used = false; + constinit KVirtualAddress g_secure_applet_memory_address = Null; + constinit KSpinLock g_secure_region_lock; + constinit bool g_secure_region_used = false; + constinit KPhysicalAddress g_secure_region_phys_addr = Null; + constinit size_t g_secure_region_size = 0; /* Global variables for randomness. */ /* Nintendo uses std::mt19937_t for randomness. */ @@ -35,7 +43,7 @@ namespace ams::kern::board::nintendo::nx { /* We will use TinyMT. */ bool g_initialized_random_generator; util::TinyMT g_random_generator; - KSpinLock g_random_lock; + constinit KSpinLock g_random_lock; ALWAYS_INLINE size_t GetRealMemorySizeForInit() { /* TODO: Move this into a header for the MC in general. */ @@ -216,6 +224,90 @@ namespace ams::kern::board::nintendo::nx { return false; } + bool SetSecureRegion(KPhysicalAddress phys_addr, size_t size) { + /* Ensure address and size are aligned. */ + if (!util::IsAligned(GetInteger(phys_addr), SecureAlignment)) { + return false; + } + if (!util::IsAligned(size, SecureAlignment)) { + return false; + } + + /* Disable interrupts and acquire the secure region lock. */ + KScopedInterruptDisable di; + KScopedSpinLock lk(g_secure_region_lock); + + /* If size is non-zero, we're allocating the secure region. Otherwise, we're freeing it. */ + if (size != 0) { + /* Verify that the secure region is free. */ + if (g_secure_region_used) { + return false; + } + + /* Set the secure region. */ + g_secure_region_used = true; + g_secure_region_phys_addr = phys_addr; + g_secure_region_size = size; + } else { + /* Verify that the secure region is in use. */ + if (!g_secure_region_used) { + return false; + } + + /* Verify that the address being freed is the secure region. */ + if (phys_addr != g_secure_region_phys_addr) { + return false; + } + + /* Clear the secure region. */ + g_secure_region_used = false; + g_secure_region_phys_addr = Null; + g_secure_region_size = 0; + } + + /* Configure the carveout with the secure monitor. */ + smc::ConfigureCarveout(1, GetInteger(phys_addr), size); + + return true; + } + + Result AllocateSecureMemoryForApplet(KVirtualAddress *out, size_t size) { + /* Verify that the size is valid. */ + R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize()); + R_UNLESS(size <= SecureAppletMemorySize, svc::ResultOutOfMemory()); + + /* Disable interrupts and acquire the secure applet lock. */ + KScopedInterruptDisable di; + KScopedSpinLock lk(g_secure_applet_lock); + + /* Check that memory is reserved for secure applet use. */ + MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address != Null); + + /* Verify that the secure applet memory isn't already being used. */ + R_UNLESS(!g_secure_applet_memory_used, svc::ResultOutOfMemory()); + + /* Return the secure applet memory. */ + g_secure_applet_memory_used = true; + *out = g_secure_applet_memory_address; + + return ResultSuccess(); + } + + void FreeSecureMemoryForApplet(KVirtualAddress address, size_t size) { + /* Disable interrupts and acquire the secure applet lock. */ + KScopedInterruptDisable di; + KScopedSpinLock lk(g_secure_applet_lock); + + /* Verify that the memory being freed is correct. */ + MESOSPHERE_ABORT_UNLESS(address == g_secure_applet_memory_address); + MESOSPHERE_ABORT_UNLESS(size <= SecureAppletMemorySize); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, PageSize)); + MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_used); + + /* Release the secure applet memory. */ + g_secure_applet_memory_used = false; + } + } /* Initialization. */ @@ -363,10 +455,10 @@ namespace ams::kern::board::nintendo::nx { /* Reserve secure applet memory. */ { MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address == Null); - MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, SecureAppletReservedMemorySize)); + MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, SecureAppletMemorySize)); constexpr auto SecureAppletAllocateOption = KMemoryManager::EncodeOption(KMemoryManager::Pool_System, KMemoryManager::Direction_FromFront); - g_secure_applet_memory_address = Kernel::GetMemoryManager().AllocateContinuous(SecureAppletReservedMemorySize / PageSize, 1, SecureAppletAllocateOption); + g_secure_applet_memory_address = Kernel::GetMemoryManager().AllocateContinuous(SecureAppletMemorySize / PageSize, 1, SecureAppletAllocateOption); MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address != Null); } } @@ -480,11 +572,71 @@ namespace ams::kern::board::nintendo::nx { } Result KSystemControl::AllocateSecureMemory(KVirtualAddress *out, size_t size, u32 pool) { - MESOSPHERE_UNIMPLEMENTED(); + /* Applet secure memory is handled separately. */ + if (pool == KMemoryManager::Pool_Applet) { + return AllocateSecureMemoryForApplet(out, size); + } + + /* Ensure the size is aligned. */ + const size_t alignment = (pool == KMemoryManager::Pool_System ? PageSize : SecureAlignment); + R_UNLESS(util::IsAligned(size, alignment), svc::ResultInvalidSize()); + + /* Allocate the memory. */ + const size_t num_pages = size / PageSize; + const KVirtualAddress vaddr = Kernel::GetMemoryManager().AllocateContinuous(num_pages, alignment / PageSize, KMemoryManager::EncodeOption(static_cast(pool), KMemoryManager::Direction_FromFront)); + R_UNLESS(vaddr != Null, svc::ResultOutOfMemory()); + + /* Open a reference to the memory. */ + Kernel::GetMemoryManager().Open(vaddr, num_pages); + + /* Ensure we don't leak references to the memory on error. */ + auto mem_guard = SCOPE_GUARD { Kernel::GetMemoryManager().Close(vaddr, num_pages); }; + + /* If the memory isn't already secure, set it as secure. */ + if (pool != KMemoryManager::Pool_System) { + /* Get the physical address. */ + const KPhysicalAddress paddr = KPageTable::GetHeapPhysicalAddress(vaddr); + MESOSPHERE_ABORT_UNLESS(paddr != Null); + + /* Set the secure region. */ + R_UNLESS(SetSecureRegion(paddr, size), svc::ResultOutOfMemory()); + } + + /* We succeeded. */ + mem_guard.Cancel(); + *out = vaddr; + return ResultSuccess(); } void KSystemControl::FreeSecureMemory(KVirtualAddress address, size_t size, u32 pool) { - MESOSPHERE_UNIMPLEMENTED(); + /* Applet secure memory is handled separately. */ + if (pool == KMemoryManager::Pool_Applet) { + return FreeSecureMemoryForApplet(address, size); + } + + /* Ensure the size is aligned. */ + const size_t alignment = (pool == KMemoryManager::Pool_System ? PageSize : SecureAlignment); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(address), alignment)); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(size, alignment)); + + /* If the memory isn't secure system, reset the secure region. */ + if (pool != KMemoryManager::Pool_System) { + /* Check that the size being freed is the current secure region size. */ + MESOSPHERE_ABORT_UNLESS(g_secure_region_size == size); + + /* Get the physical address. */ + const KPhysicalAddress paddr = KPageTable::GetHeapPhysicalAddress(address); + MESOSPHERE_ABORT_UNLESS(paddr != Null); + + /* Check that the memory being freed is the current secure region. */ + MESOSPHERE_ABORT_UNLESS(paddr == g_secure_region_phys_addr); + + /* Free the secure region. */ + MESOSPHERE_ABORT_UNLESS(SetSecureRegion(paddr, 0)); + } + + /* Close the secure region's pages. */ + Kernel::GetMemoryManager().Close(address, size / PageSize); } } \ No newline at end of file diff --git a/libraries/libmesosphere/source/svc/kern_svc_info.cpp b/libraries/libmesosphere/source/svc/kern_svc_info.cpp index 465a29143..e8a3312b1 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_info.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_info.cpp @@ -22,9 +22,6 @@ namespace ams::kern::svc { namespace { Result GetInfo(u64 *out, ams::svc::InfoType info_type, ams::svc::Handle handle, u64 info_subtype) { - MESOSPHERE_LOG("GetInfo(%p, %u, %08x, %lu) was called\n", out, static_cast(info_type), static_cast(handle), info_subtype); - ON_SCOPE_EXIT{ MESOSPHERE_LOG("GetInfo returned %016lx\n", *out); }; - switch (info_type) { case ams::svc::InfoType_CoreMask: case ams::svc::InfoType_PriorityMask: @@ -38,11 +35,14 @@ namespace ams::kern::svc { case ams::svc::InfoType_AslrRegionSize: case ams::svc::InfoType_StackRegionAddress: case ams::svc::InfoType_StackRegionSize: + case ams::svc::InfoType_SystemResourceSizeTotal: + case ams::svc::InfoType_SystemResourceSizeUsed: case ams::svc::InfoType_ProgramId: case ams::svc::InfoType_InitialProcessIdRange: case ams::svc::InfoType_UserExceptionContextAddress: case ams::svc::InfoType_TotalNonSystemMemorySize: case ams::svc::InfoType_UsedNonSystemMemorySize: + case ams::svc::InfoType_IsApplication: { /* These info types don't support non-zero subtypes. */ R_UNLESS(info_subtype == 0, svc::ResultInvalidCombination()); @@ -88,6 +88,12 @@ namespace ams::kern::svc { case ams::svc::InfoType_StackRegionSize: *out = process->GetPageTable().GetStackRegionSize(); break; + case ams::svc::InfoType_SystemResourceSizeTotal: + *out = process->GetTotalSystemResourceSize(); + break; + case ams::svc::InfoType_SystemResourceSizeUsed: + *out = process->GetUsedSystemResourceSize(); + break; case ams::svc::InfoType_ProgramId: *out = process->GetProgramId(); break; @@ -103,6 +109,9 @@ namespace ams::kern::svc { case ams::svc::InfoType_UsedNonSystemMemorySize: *out = process->GetUsedNonSystemUserPhysicalMemorySize(); break; + case ams::svc::InfoType_IsApplication: + *out = process->IsApplication(); + break; MESOSPHERE_UNREACHABLE_DEFAULT_CASE(); } } @@ -157,6 +166,11 @@ namespace ams::kern::svc { } break; default: + { + /* For debug, until all infos are implemented. */ + MESOSPHERE_LOG("GetInfo(%p, %u, %08x, %lu) was called\n", out, static_cast(info_type), static_cast(handle), info_subtype); + MESOSPHERE_UNIMPLEMENTED(); + } return svc::ResultInvalidEnumValue(); } @@ -176,9 +190,6 @@ namespace ams::kern::svc { } Result GetSystemInfo(u64 *out, ams::svc::SystemInfoType info_type, ams::svc::Handle handle, u64 info_subtype) { - MESOSPHERE_LOG("GetSystemInfo(%p, %u, %08x, %lu) was called\n", out, static_cast(info_type), static_cast(handle), info_subtype); - ON_SCOPE_EXIT{ MESOSPHERE_LOG("GetSystemInfo returned %016lx\n", *out); }; - switch (info_type) { case ams::svc::SystemInfoType_TotalPhysicalMemorySize: case ams::svc::SystemInfoType_UsedPhysicalMemorySize: