From ca9327a1201cec5471b08fc3caea36eea318e454 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 10 Jul 2020 23:30:15 -0700 Subject: [PATCH] kern: implement SvcSleepThread for ns > 0 --- .../include/mesosphere/kern_k_scheduler.hpp | 5 ++- .../include/mesosphere/kern_k_thread.hpp | 2 ++ .../libmesosphere/source/kern_k_scheduler.cpp | 11 ++++++ .../libmesosphere/source/kern_k_thread.cpp | 29 +++++++++++++++ .../source/svc/kern_svc_thread.cpp | 35 +++++++++++++++++-- 5 files changed, 79 insertions(+), 3 deletions(-) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp index ba834a8b8..c1048ef16 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp @@ -122,8 +122,11 @@ namespace ams::kern { static NOINLINE void OnThreadPriorityChanged(KThread *thread, s32 old_priority); static NOINLINE void OnThreadAffinityMaskChanged(KThread *thread, const KAffinityMask &old_affinity, s32 old_core); - /* TODO: Yield operations */ static NOINLINE void RotateScheduledQueue(s32 priority, s32 core_id); + + static NOINLINE void YieldWithoutCoreMigration(); + static NOINLINE void YieldWithCoreMigration(); + static NOINLINE void YieldToAnyThread(); private: /* Instanced private API. */ void ScheduleImpl(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 5c7febd1a..a1a6f2bdb 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -385,6 +385,8 @@ namespace ams::kern { Result Run(); void Exit(); + Result Sleep(s64 timeout); + ALWAYS_INLINE void *GetStackTop() const { return reinterpret_cast(this->kernel_stack_top) - 1; } ALWAYS_INLINE void *GetKernelStackTop() const { return this->kernel_stack_top; } diff --git a/libraries/libmesosphere/source/kern_k_scheduler.cpp b/libraries/libmesosphere/source/kern_k_scheduler.cpp index 6208b8ab0..50834f464 100644 --- a/libraries/libmesosphere/source/kern_k_scheduler.cpp +++ b/libraries/libmesosphere/source/kern_k_scheduler.cpp @@ -401,5 +401,16 @@ namespace ams::kern { SetSchedulerUpdateNeeded(); } + void KScheduler::YieldWithoutCoreMigration() { + MESOSPHERE_UNIMPLEMENTED(); + } + + void KScheduler::YieldWithCoreMigration() { + MESOSPHERE_UNIMPLEMENTED(); + } + + void KScheduler::YieldToAnyThread() { + MESOSPHERE_UNIMPLEMENTED(); + } } diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index cc0ed73b4..a0ebf689e 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -664,6 +664,35 @@ namespace ams::kern { MESOSPHERE_PANIC("KThread::Exit() would return"); } + Result KThread::Sleep(s64 timeout) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(!KScheduler::IsSchedulerLockedByCurrentThread()); + MESOSPHERE_ASSERT(this == GetCurrentThreadPointer()); + MESOSPHERE_ASSERT(timeout > 0); + + KHardwareTimer *timer; + { + /* Setup the scheduling lock and sleep. */ + KScopedSchedulerLockAndSleep slp(std::addressof(timer), this, timeout); + + /* Check if the thread should terminate. */ + if (this->IsTerminationRequested()) { + slp.CancelSleep(); + return svc::ResultTerminationRequested(); + } + + /* Mark the thread as waiting. */ + this->SetState(KThread::ThreadState_Waiting); + } + + /* The lock/sleep is done. */ + + /* Cancel the timer. */ + timer->CancelTask(this); + + return ResultSuccess(); + } + void KThread::SetState(ThreadState state) { MESOSPHERE_ASSERT_THIS(); diff --git a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp index 388a452af..19238f717 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_thread.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_thread.cpp @@ -86,6 +86,37 @@ namespace ams::kern::svc { MESOSPHERE_PANIC("Process survived call to exit"); } + void SleepThread(int64_t ns) { + /* When the input tick is positive, sleep. */ + if (AMS_LIKELY(ns > 0)) { + /* Convert the timeout from nanoseconds to ticks. */ + /* NOTE: Nintendo does not use this conversion logic in WaitSynchronization... */ + s64 timeout; + + const ams::svc::Tick offset_tick(TimeSpan::FromNanoSeconds(ns)); + if (AMS_LIKELY(offset_tick > 0)) { + timeout = KHardwareTimer::GetTick() + offset_tick + 2; + if (AMS_UNLIKELY(timeout <= 0)) { + timeout = std::numeric_limits::max(); + } + } else { + timeout = std::numeric_limits::max(); + } + + /* Sleep. */ + /* NOTE: Nintendo does not check the result of this sleep. */ + GetCurrentThread().Sleep(timeout); + } else if (ns == ams::svc::YieldType_WithoutCoreMigration) { + KScheduler::YieldWithoutCoreMigration(); + } else if (ns == ams::svc::YieldType_WithCoreMigration) { + KScheduler::YieldWithCoreMigration(); + } else if (ns == ams::svc::YieldType_ToAnyThread) { + KScheduler::YieldToAnyThread(); + } else { + /* Nintendo does nothing at all if an otherwise invalid value is passed. */ + } + } + Result GetThreadPriority(int32_t *out_priority, ams::svc::Handle thread_handle) { /* Get the thread from its handle. */ KScopedAutoObject thread = GetCurrentProcess().GetHandleTable().GetObject(thread_handle); @@ -123,7 +154,7 @@ namespace ams::kern::svc { } void SleepThread64(int64_t ns) { - MESOSPHERE_PANIC("Stubbed SvcSleepThread64 was called."); + return SleepThread(ns); } Result GetThreadPriority64(int32_t *out_priority, ams::svc::Handle thread_handle) { @@ -177,7 +208,7 @@ namespace ams::kern::svc { } void SleepThread64From32(int64_t ns) { - MESOSPHERE_PANIC("Stubbed SvcSleepThread64From32 was called."); + return SleepThread(ns); } Result GetThreadPriority64From32(int32_t *out_priority, ams::svc::Handle thread_handle) {