From 6faa3534bff5b35b7e30494f42f4893d3442e790 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 7 Apr 2021 15:30:13 -0700 Subject: [PATCH] kern: update pinning semantics for terminating threads --- .../include/mesosphere/kern_k_process.hpp | 5 +-- .../arch/arm64/kern_exception_handlers.cpp | 5 +++ .../libmesosphere/source/kern_k_process.cpp | 34 ++++++++++++++++--- .../libmesosphere/source/kern_k_thread.cpp | 19 ++++++----- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 43d87cbcd..af3e59c7b 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -127,14 +127,14 @@ namespace ams::kern { Result StartTermination(); void FinishTermination(); - void PinThread(s32 core_id, KThread *thread) { + ALWAYS_INLINE void PinThread(s32 core_id, KThread *thread) { MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); MESOSPHERE_ASSERT(thread != nullptr); MESOSPHERE_ASSERT(m_pinned_threads[core_id] == nullptr); m_pinned_threads[core_id] = thread; } - void UnpinThread(s32 core_id, KThread *thread) { + ALWAYS_INLINE void UnpinThread(s32 core_id, KThread *thread) { MESOSPHERE_UNUSED(thread); MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); MESOSPHERE_ASSERT(thread != nullptr); @@ -340,6 +340,7 @@ namespace ams::kern { void PinCurrentThread(); void UnpinCurrentThread(); + void UnpinThread(KThread *thread); Result SignalToAddress(KProcessAddress address) { return m_cond_var.SignalToAddress(address); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index 263a304ea..2bb27ae0d 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -521,6 +521,11 @@ namespace ams::kern::arch::arm64 { { KScopedInterruptEnable ei; + /* Terminate the thread, if we should. */ + if (GetCurrentThread().IsTerminationRequested()) { + GetCurrentThread().Exit(); + } + HandleUserException(context, esr, far, afsr0, afsr1, data); } } else { diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 97a069a8f..dacd1303e 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -34,6 +34,13 @@ namespace ams::kern { KScopedLightLock proc_lk(process->GetListLock()); KScopedSchedulerLock sl; + if (thread_to_not_terminate != nullptr && process->GetPinnedThread(GetCurrentCoreId()) == thread_to_not_terminate) { + /* NOTE: Here Nintendo unpins the current thread instead of the thread_to_not_terminate. */ + /* This is valid because the only caller which uses non-nullptr as argument uses GetCurrentThreadPointer(), */ + /* but it's still notable because it seems incorrect at first glance. */ + process->UnpinCurrentThread(); + } + auto &thread_list = process->GetThreadList(); for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { if (KThread *thread = std::addressof(*it); thread != thread_to_not_terminate) { @@ -1020,12 +1027,15 @@ namespace ams::kern { const s32 core_id = GetCurrentCoreId(); KThread *cur_thread = GetCurrentThreadPointer(); - /* Pin it. */ - this->PinThread(core_id, cur_thread); - cur_thread->Pin(); + /* If the thread isn't terminated, pin it. */ + if (!cur_thread->IsTerminationRequested()) { + /* Pin it. */ + this->PinThread(core_id, cur_thread); + cur_thread->Pin(); - /* An update is needed. */ - KScheduler::SetSchedulerUpdateNeeded(); + /* An update is needed. */ + KScheduler::SetSchedulerUpdateNeeded(); + } } void KProcess::UnpinCurrentThread() { @@ -1043,6 +1053,20 @@ namespace ams::kern { KScheduler::SetSchedulerUpdateNeeded(); } + void KProcess::UnpinThread(KThread *thread) { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + + /* Get the thread's core id. */ + const auto core_id = thread->GetActiveCore(); + + /* Unpin it. */ + this->UnpinThread(core_id, thread); + thread->Unpin(); + + /* An update is needed. */ + KScheduler::SetSchedulerUpdateNeeded(); + } + Result KProcess::GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer out_thread_ids, s32 max_out_count) { /* Lock the list. */ KScopedLightLock lk(m_list_lock); diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index fbc565eaa..de857658b 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -483,19 +483,17 @@ namespace ams::kern { } /* Allow performing thread suspension (if termination hasn't been requested). */ - { + if (!this->IsTerminationRequested()) { /* Update our allow flags. */ - if (!this->IsTerminationRequested()) { - m_suspend_allowed_flags |= (1 << (SuspendType_Thread + ThreadState_SuspendShift)); - } + m_suspend_allowed_flags |= (1 << (SuspendType_Thread + ThreadState_SuspendShift)); /* Update our state. */ this->UpdateState(); - } - /* Update our SVC access permissions. */ - MESOSPHERE_ASSERT(m_parent != nullptr); - m_parent->CopyUnpinnedSvcPermissionsTo(this->GetStackParameters()); + /* Update our SVC access permissions. */ + MESOSPHERE_ASSERT(m_parent != nullptr); + m_parent->CopyUnpinnedSvcPermissionsTo(this->GetStackParameters()); + } /* Resume any threads that began waiting on us while we were pinned. */ for (auto it = m_pinned_waiter_list.begin(); it != m_pinned_waiter_list.end(); ++it) { @@ -1218,6 +1216,11 @@ namespace ams::kern { /* Register the terminating dpc. */ this->RegisterDpc(DpcFlag_Terminating); + /* If the thread is pinned, unpin it. */ + if (this->GetStackParameters().is_pinned) { + this->GetOwnerProcess()->UnpinThread(this); + } + /* If the thread is suspended, continue it. */ if (this->IsSuspended()) { m_suspend_allowed_flags = 0;