diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp index 38be0efc7..8900797ec 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_handle_table.hpp @@ -211,6 +211,49 @@ namespace ams::kern { static_assert(std::is_base_of::value); return this->Register(handle, obj, obj->GetTypeObj().GetClassToken()); } + + template + ALWAYS_INLINE bool GetMultipleObjects(T **out, const ams::svc::Handle *handles, size_t num_handles) const { + /* Try to convert and open all the handles. */ + size_t num_opened; + { + /* Lock the table. */ + KScopedDisableDispatch dd; + KScopedSpinLock lk(this->lock); + for (num_opened = 0; num_opened < num_handles; num_opened++) { + /* Get the current handle. */ + const auto cur_handle = handles[num_opened]; + + /* Get the object for the current handle. */ + KAutoObject *cur_object = this->GetObjectImpl(cur_handle); + if (AMS_UNLIKELY(cur_object == nullptr)) { + break; + } + + /* Cast the current object to the desired type. */ + T *cur_t = cur_object->DynamicCast(); + if (AMS_UNLIKELY(cur_t == nullptr)) { + break; + } + + /* Open a reference to the current object. */ + cur_t->Open(); + out[num_opened] = cur_t; + } + } + + /* If we converted every object, succeed. */ + if (AMS_LIKELY(num_opened == num_handles)) { + return true; + } + + /* If we didn't convert entry object, close the ones we opened. */ + for (size_t i = 0; i < num_opened; i++) { + out[i]->Close(); + } + + return false; + } private: NOINLINE Result Add(ams::svc::Handle *out_handle, KAutoObject *obj, u16 type); NOINLINE void Register(ams::svc::Handle handle, KAutoObject *obj, u16 type); @@ -318,49 +361,6 @@ namespace ams::kern { *out_handle = EncodeHandle(index, entry->GetLinearId()); return entry->GetObject(); } - - template - ALWAYS_INLINE bool GetMultipleObjects(T **out, const ams::svc::Handle *handles, size_t num_handles) const { - /* Try to convert and open all the handles. */ - size_t num_opened; - { - /* Lock the table. */ - KScopedDisableDispatch dd; - KScopedSpinLock lk(this->lock); - for (num_opened = 0; num_opened < num_handles; num_opened++) { - /* Get the current handle. */ - const auto cur_handle = handles[num_opened]; - - /* Get the object for the current handle. */ - KAutoObject *cur_object = this->GetObjectImpl(cur_handle); - if (AMS_UNLIKELY(cur_object == nullptr)) { - break; - } - - /* Cast the current object to the desired type. */ - T *cur_t = cur_object->DynamicCast(); - if (AMS_UNLIKELY(cur_t == nullptr)) { - break; - } - - /* Open a reference to the current object. */ - cur_t->Open(); - out[num_opened] = cur_t; - } - } - - /* If we converted every object, succeed. */ - if (AMS_LIKELY(num_opened == num_handles)) { - return true; - } - - /* If we didn't convert entry object, close the ones we opened. */ - for (size_t i = 0; i < num_opened; i++) { - out[i]->Close(); - } - - return false; - } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp index 9f7934cd9..31532f9f4 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_synchronization_object.hpp @@ -42,8 +42,8 @@ namespace ams::kern { virtual bool IsSignaled() const = 0; virtual void DebugWaiters(); - iterator AddWaiterThread(KThread *thread); - iterator RemoveWaiterThread(iterator it); + iterator RegisterWaitingThread(KThread *thread); + iterator UnregisterWaitingThread(iterator it); iterator begin(); iterator end(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 8e3ace54e..b7bb472dd 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -113,8 +113,8 @@ namespace ams::kern { private: static constexpr size_t PriorityInheritanceCountMax = 10; union SyncObjectBuffer { - KSynchronizationObject *sync_objects[ams::svc::MaxWaitSynchronizationHandleCount]; - ams::svc::Handle handles[ams::svc::MaxWaitSynchronizationHandleCount * (sizeof(KSynchronizationObject *) / sizeof(ams::svc::Handle))]; + KSynchronizationObject *sync_objects[ams::svc::ArgumentHandleCountMax]; + ams::svc::Handle handles[ams::svc::ArgumentHandleCountMax * (sizeof(KSynchronizationObject *) / sizeof(ams::svc::Handle))]; constexpr SyncObjectBuffer() : sync_objects() { /* ... */ } }; @@ -310,10 +310,25 @@ namespace ams::kern { constexpr KThread *GetLockOwner() const { return this->lock_owner; } constexpr void SetSyncedObject(KSynchronizationObject *obj, Result wait_res) { + MESOSPHERE_ASSERT_THIS(); + this->synced_object = obj; this->wait_result = wait_res; } + constexpr Result GetWaitResult(KSynchronizationObject **out) const { + MESOSPHERE_ASSERT_THIS(); + + *out = this->synced_object; + return this->wait_result; + } + + bool IsWaitCancelled() const { return this->wait_cancelled; } + void ClearWaitCancelled() { this->wait_cancelled = false; } + + void ClearCancellable() { this->cancellable = false; } + void SetCancellable() { this->cancellable = true; } + bool HasWaiters() const { return !this->waiter_list.empty(); } constexpr s64 GetLastScheduledTick() const { return this->last_scheduled_tick; } @@ -325,6 +340,9 @@ namespace ams::kern { constexpr KProcessAddress GetThreadLocalRegionAddress() const { return this->tls_address; } constexpr void *GetThreadLocalRegionHeapAddress() const { return this->tls_heap_address; } + constexpr KSynchronizationObject **GetSynchronizationObjectBuffer() { return std::addressof(this->sync_object_buffer.sync_objects[0]); } + constexpr ams::svc::Handle *GetHandleBuffer() { return std::addressof(this->sync_object_buffer.handles[sizeof(this->sync_object_buffer.sync_objects) / sizeof(ams::svc::Handle) - ams::svc::ArgumentHandleCountMax]); } + constexpr u16 GetUserPreemptionState() const { return *GetPointer(this->tls_address + 0x100); } constexpr void SetKernelPreemptionState(u16 state) const { *GetPointer(this->tls_address + 0x100 + sizeof(u16)) = state; } diff --git a/libraries/libmesosphere/source/kern_k_synchronization.cpp b/libraries/libmesosphere/source/kern_k_synchronization.cpp index 46817e035..8f611269d 100644 --- a/libraries/libmesosphere/source/kern_k_synchronization.cpp +++ b/libraries/libmesosphere/source/kern_k_synchronization.cpp @@ -20,7 +20,87 @@ namespace ams::kern { Result KSynchronization::Wait(s32 *out_index, KSynchronizationObject **objects, const s32 num_objects, s64 timeout) { MESOSPHERE_ASSERT_THIS(); - MESOSPHERE_UNIMPLEMENTED(); + /* Allocate space on stack for thread iterators. */ + KSynchronizationObject::iterator *thread_iters = static_cast(__builtin_alloca(sizeof(KSynchronizationObject::iterator) * num_objects)); + + /* Prepare for wait. */ + KThread *thread = GetCurrentThreadPointer(); + s32 sync_index = -1; + KHardwareTimer *timer; + + { + /* Setup the scheduling lock and sleep. */ + KScopedSchedulerLockAndSleep slp(std::addressof(timer), thread, timeout); + + /* Check if any of the objects are already signaled. */ + for (auto i = 0; i < num_objects; ++i) { + AMS_ASSERT(objects[i] != nullptr); + + if (objects[i]->IsSignaled()) { + *out_index = i; + slp.CancelSleep(); + return ResultSuccess(); + } + } + + /* Check if the timeout is zero. */ + if (timeout == 0) { + slp.CancelSleep(); + return svc::ResultTimedOut(); + } + + /* Check if the thread should terminate. */ + if (thread->IsTerminationRequested()) { + slp.CancelSleep(); + return svc::ResultTerminationRequested(); + } + + /* Check if waiting was canceled. */ + if (thread->IsWaitCancelled()) { + slp.CancelSleep(); + thread->ClearWaitCancelled(); + return svc::ResultCancelled(); + } + + /* Add the waiters. */ + for (auto i = 0; i < num_objects; ++i) { + thread_iters[i] = objects[i]->RegisterWaitingThread(thread); + } + + /* Mark the thread as waiting. */ + thread->SetCancellable(); + thread->SetSyncedObject(nullptr, svc::ResultTimedOut()); + thread->SetState(KThread::ThreadState_Waiting); + } + + /* The lock/sleep is done, so we should be able to get our result. */ + + /* Thread is no longer cancellable. */ + thread->ClearCancellable(); + + /* Cancel the timer as needed. */ + if (timer != nullptr) { + timer->CancelTask(thread); + } + + /* Get the wait result. */ + Result wait_result; + { + KScopedSchedulerLock lk; + KSynchronizationObject *synced_obj; + wait_result = thread->GetWaitResult(std::addressof(synced_obj)); + + for (auto i = 0; i < num_objects; ++i) { + objects[i]->UnregisterWaitingThread(thread_iters[i]); + if (objects[i] == synced_obj) { + sync_index = i; + } + } + } + + /* Set output. */ + *out_index = sync_index; + return wait_result; } void KSynchronization::OnAvailable(KSynchronizationObject *object) { diff --git a/libraries/libmesosphere/source/kern_k_synchronization_object.cpp b/libraries/libmesosphere/source/kern_k_synchronization_object.cpp index 9b093f1a8..adfd71ded 100644 --- a/libraries/libmesosphere/source/kern_k_synchronization_object.cpp +++ b/libraries/libmesosphere/source/kern_k_synchronization_object.cpp @@ -42,13 +42,13 @@ namespace ams::kern { MESOSPHERE_TODO("Do useful debug operation here."); } - KSynchronizationObject::iterator KSynchronizationObject::AddWaiterThread(KThread *thread) { + KSynchronizationObject::iterator KSynchronizationObject::RegisterWaitingThread(KThread *thread) { MESOSPHERE_ASSERT_THIS(); return this->thread_list.insert(this->thread_list.end(), *thread); } - KSynchronizationObject::iterator KSynchronizationObject::RemoveWaiterThread(KSynchronizationObject::iterator it) { + KSynchronizationObject::iterator KSynchronizationObject::UnregisterWaitingThread(KSynchronizationObject::iterator it) { MESOSPHERE_ASSERT_THIS(); return this->thread_list.erase(it); diff --git a/libraries/libmesosphere/source/svc/kern_svc_synchronization.cpp b/libraries/libmesosphere/source/svc/kern_svc_synchronization.cpp index 33b150955..8e2c1524e 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_synchronization.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_synchronization.cpp @@ -27,6 +27,58 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result WaitSynchronizationImpl(int32_t *out_index, KSynchronizationObject **objs, int32_t num_handles, int64_t timeout_ns) { + /* Convert the timeout from nanoseconds to ticks. */ + s64 timeout; + if (timeout_ns > 0) { + u64 ticks = KHardwareTimer::GetTick(); + ticks += ams::svc::Tick(TimeSpan::FromNanoSeconds(timeout_ns)); + ticks += 2; + + timeout = ticks; + } else { + timeout = timeout_ns; + } + + return Kernel::GetSynchronization().Wait(out_index, objs, num_handles, timeout); + } + + Result WaitSynchronization(int32_t *out_index, KUserPointer user_handles, int32_t num_handles, int64_t timeout_ns) { + /* Ensure number of handles is valid. */ + R_UNLESS(0 <= num_handles && num_handles <= ams::svc::ArgumentHandleCountMax, svc::ResultOutOfRange()); + + /* Get the synchronization context. */ + auto &handle_table = GetCurrentProcess().GetHandleTable(); + KSynchronizationObject **objs = GetCurrentThread().GetSynchronizationObjectBuffer(); + ams::svc::Handle *handles = GetCurrentThread().GetHandleBuffer(); + + /* Copy user handles. */ + if (num_handles > 0) { + /* Ensure that we can try to get the handles. */ + R_UNLESS(GetCurrentProcess().GetPageTable().Contains(KProcessAddress(user_handles.GetUnsafePointer()), num_handles * sizeof(ams::svc::Handle)), svc::ResultInvalidPointer()); + + /* Get the handles. */ + R_TRY(user_handles.CopyArrayTo(handles, num_handles)); + + /* Convert the handles to objects. */ + R_UNLESS(handle_table.GetMultipleObjects(objs, handles, num_handles), svc::ResultInvalidHandle()); + } + + /* Ensure handles are closed when we're done. */ + ON_SCOPE_EXIT { + for (auto i = 0; i < num_handles; ++i) { + objs[i]->Close(); + } + }; + + /* Wait on the objects. */ + R_TRY_CATCH(WaitSynchronizationImpl(out_index, objs, num_handles, timeout_ns)) { + R_CONVERT(svc::ResultSessionClosed, ResultSuccess()) + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + } /* ============================= 64 ABI ============================= */ @@ -39,8 +91,10 @@ namespace ams::kern::svc { MESOSPHERE_PANIC("Stubbed SvcResetSignal64 was called."); } - Result WaitSynchronization64(int32_t *out_index, KUserPointer handles, int32_t numHandles, int64_t timeout_ns) { - MESOSPHERE_PANIC("Stubbed SvcWaitSynchronization64 was called."); + Result WaitSynchronization64(int32_t *out_index, KUserPointer handles, int32_t num_handles, int64_t timeout_ns) { + Result result = WaitSynchronization(out_index, handles, num_handles, timeout_ns); + MESOSPHERE_LOG("WaitSynchronization returned %08x\n", result.GetValue()); + return result; } Result CancelSynchronization64(ams::svc::Handle handle) { @@ -61,8 +115,8 @@ namespace ams::kern::svc { MESOSPHERE_PANIC("Stubbed SvcResetSignal64From32 was called."); } - Result WaitSynchronization64From32(int32_t *out_index, KUserPointer handles, int32_t numHandles, int64_t timeout_ns) { - MESOSPHERE_PANIC("Stubbed SvcWaitSynchronization64From32 was called."); + Result WaitSynchronization64From32(int32_t *out_index, KUserPointer handles, int32_t num_handles, int64_t timeout_ns) { + return WaitSynchronization(out_index, handles, num_handles, timeout_ns); } Result CancelSynchronization64From32(ams::svc::Handle handle) { diff --git a/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp b/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp index 56b7f8278..a98ba09e7 100644 --- a/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp +++ b/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp @@ -110,8 +110,8 @@ namespace ams::svc::aarch64::lp64 { return ::svcResetSignal(handle); } - ALWAYS_INLINE Result WaitSynchronization(int32_t *out_index, ::ams::svc::UserPointer handles, int32_t numHandles, int64_t timeout_ns) { - return ::svcWaitSynchronization(out_index, handles.GetPointerUnsafe(), numHandles, timeout_ns); + ALWAYS_INLINE Result WaitSynchronization(int32_t *out_index, ::ams::svc::UserPointer handles, int32_t num_handles, int64_t timeout_ns) { + return ::svcWaitSynchronization(out_index, handles.GetPointerUnsafe(), num_handles, timeout_ns); } ALWAYS_INLINE Result CancelSynchronization(::ams::svc::Handle handle) { diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp index 03918c95f..44cb6f138 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.hpp @@ -21,7 +21,7 @@ namespace ams::os::impl { class WaitableManagerHorizonImpl { public: - static constexpr size_t MaximumHandleCount = svc::MaxWaitSynchronizationHandleCount; + static constexpr size_t MaximumHandleCount = static_cast(ArgumentHandleCountMax); private: Handle handle; private: diff --git a/libraries/libvapours/include/vapours/svc/svc_common.hpp b/libraries/libvapours/include/vapours/svc/svc_common.hpp index 9e4fc430d..9f68ad73d 100644 --- a/libraries/libvapours/include/vapours/svc/svc_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_common.hpp @@ -32,7 +32,7 @@ namespace ams::svc { HandleWaitMask = (1u << 30), }; - constexpr inline size_t MaxWaitSynchronizationHandleCount = 0x40; + constexpr inline s32 ArgumentHandleCountMax = 0x40; constexpr inline s64 WaitInfinite = -1; diff --git a/libraries/libvapours/include/vapours/svc/svc_definitions.hpp b/libraries/libvapours/include/vapours/svc/svc_definitions.hpp index dee20ba88..f44eda35f 100644 --- a/libraries/libvapours/include/vapours/svc/svc_definitions.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_definitions.hpp @@ -52,7 +52,7 @@ HANDLER(0x15, Result, CreateTransferMemory, OUTPUT(::ams::svc::Handle, out_handle), INPUT(::ams::svc::Address, address), INPUT(::ams::svc::Size, size), INPUT(::ams::svc::MemoryPermission, map_perm)) \ HANDLER(0x16, Result, CloseHandle, INPUT(::ams::svc::Handle, handle)) \ HANDLER(0x17, Result, ResetSignal, INPUT(::ams::svc::Handle, handle)) \ - HANDLER(0x18, Result, WaitSynchronization, OUTPUT(int32_t, out_index), INPTR(::ams::svc::Handle, handles), INPUT(int32_t, numHandles), INPUT(int64_t, timeout_ns)) \ + HANDLER(0x18, Result, WaitSynchronization, OUTPUT(int32_t, out_index), INPTR(::ams::svc::Handle, handles), INPUT(int32_t, num_handles), INPUT(int64_t, timeout_ns)) \ HANDLER(0x19, Result, CancelSynchronization, INPUT(::ams::svc::Handle, handle)) \ HANDLER(0x1A, Result, ArbitrateLock, INPUT(::ams::svc::Handle, thread_handle), INPUT(::ams::svc::Address, address), INPUT(uint32_t, tag)) \ HANDLER(0x1B, Result, ArbitrateUnlock, INPUT(::ams::svc::Address, address)) \