diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index 59d5e34e1..d2393ad7b 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/impl/os_memory_fence_api.os.horizon.hpp b/libraries/libstratosphere/include/stratosphere/os/impl/os_memory_fence_api.os.horizon.hpp new file mode 100644 index 000000000..45e687102 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/impl/os_memory_fence_api.os.horizon.hpp @@ -0,0 +1,41 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::os::impl { + + #if defined(ATMOSPHERE_ARCH_ARM64) + + ALWAYS_INLINE void FenceMemoryStoreStore() { __asm__ __volatile__("dmb ishst" ::: "memory"); } + ALWAYS_INLINE void FenceMemoryStoreLoad() { __asm__ __volatile__("dmb ish" ::: "memory"); } + ALWAYS_INLINE void FenceMemoryStoreAny() { __asm__ __volatile__("dmb ish" ::: "memory"); } + + ALWAYS_INLINE void FenceMemoryLoadStore() { __asm__ __volatile__("dmb ishld" ::: "memory"); } + ALWAYS_INLINE void FenceMemoryLoadLoad() { __asm__ __volatile__("dmb ishld" ::: "memory"); } + ALWAYS_INLINE void FenceMemoryLoadAny() { __asm__ __volatile__("dmb ishld" ::: "memory"); } + + ALWAYS_INLINE void FenceMemoryAnyStore() { __asm__ __volatile__("dmb ish" ::: "memory"); } + ALWAYS_INLINE void FenceMemoryAnyLoad() { __asm__ __volatile__("dmb ish" ::: "memory"); } + ALWAYS_INLINE void FenceMemoryAnyAny() { __asm__ __volatile__("dmb ish" ::: "memory"); } + + #else + + #error "Unknown architecture for os::impl::FenceMemory* (Horizon)" + + #endif + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_fence.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_fence.hpp new file mode 100644 index 000000000..7e071095a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_fence.hpp @@ -0,0 +1,17 @@ +/* + * 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 . + */ +#pragma once +#include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_memory_fence_api.hpp b/libraries/libstratosphere/include/stratosphere/os/os_memory_fence_api.hpp new file mode 100644 index 000000000..4be7ee8bc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_memory_fence_api.hpp @@ -0,0 +1,39 @@ +/* + * 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 . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_OS_HORIZON) + #include +#else + #error "Unknown os for os::MemoryFence*" +#endif + +namespace ams::os { + + ALWAYS_INLINE void FenceMemoryStoreStore() { return impl::FenceMemoryStoreStore(); } + ALWAYS_INLINE void FenceMemoryStoreLoad() { return impl::FenceMemoryStoreLoad(); } + ALWAYS_INLINE void FenceMemoryStoreAny() { return impl::FenceMemoryStoreAny(); } + + ALWAYS_INLINE void FenceMemoryLoadStore() { return impl::FenceMemoryLoadStore(); } + ALWAYS_INLINE void FenceMemoryLoadLoad() { return impl::FenceMemoryLoadLoad(); } + ALWAYS_INLINE void FenceMemoryLoadAny() { return impl::FenceMemoryLoadAny(); } + + ALWAYS_INLINE void FenceMemoryAnyStore() { return impl::FenceMemoryLoadStore(); } + ALWAYS_INLINE void FenceMemoryAnyLoad() { return impl::FenceMemoryLoadLoad(); } + ALWAYS_INLINE void FenceMemoryAnyAny() { return impl::FenceMemoryLoadAny(); } + +} diff --git a/libraries/libstratosphere/include/stratosphere/os/os_sdk_reply_and_receive.hpp b/libraries/libstratosphere/include/stratosphere/os/os_sdk_reply_and_receive.hpp new file mode 100644 index 000000000..a88967c48 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_sdk_reply_and_receive.hpp @@ -0,0 +1,26 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::os { + + struct WaitableHolderType; + struct WaitableManagerType; + + Result SdkReplyAndReceive(os::WaitableHolderType **out, Handle reply_target, WaitableManagerType *manager); + +} diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp index e5a2a48db..b851503e4 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.cpp @@ -20,23 +20,32 @@ namespace ams::os::impl { - WaitableHolderBase *WaitableManagerImpl::WaitAnyImpl(bool infinite, TimeSpan timeout) { + Result WaitableManagerImpl::WaitAnyImpl(WaitableHolderBase **out, bool infinite, TimeSpan timeout, bool reply, Handle reply_target) { /* Prepare for processing. */ this->signaled_holder = nullptr; this->target_impl.SetCurrentThreadHandleForCancelWait(); - WaitableHolderBase *result = this->LinkHoldersToObjectList(); + WaitableHolderBase *holder = this->LinkHoldersToObjectList(); /* Check if we've been signaled. */ { std::scoped_lock lk(this->cs_wait); if (this->signaled_holder != nullptr) { - result = this->signaled_holder; + holder = this->signaled_holder; } } /* Process object array. */ - if (result == nullptr) { - result = this->WaitAnyHandleImpl(infinite, timeout); + Result wait_result = ResultSuccess(); + if (holder != nullptr) { + if (reply && reply_target != svc::InvalidHandle) { + s32 index; + wait_result = this->target_impl.TimedReplyAndReceive(std::addressof(index), nullptr, 0, 0, reply_target, TimeSpan::FromNanoSeconds(0)); + if (R_FAILED(wait_result)) { + holder = nullptr; + } + } + } else { + wait_result = this->WaitAnyHandleImpl(std::addressof(holder), infinite, timeout, reply, reply_target); } /* Unlink holders from the current object list. */ @@ -44,10 +53,13 @@ namespace ams::os::impl { this->target_impl.ClearCurrentThreadHandleForCancelWait(); - return result; + /* Set output holder. */ + *out = holder; + + return wait_result; } - WaitableHolderBase *WaitableManagerImpl::WaitAnyHandleImpl(bool infinite, TimeSpan timeout) { + Result WaitableManagerImpl::WaitAnyHandleImpl(WaitableHolderBase **out, bool infinite, TimeSpan timeout, bool reply, Handle reply_target) { Handle object_handles[MaximumHandleCount]; WaitableHolderBase *objects[MaximumHandleCount]; @@ -60,18 +72,30 @@ namespace ams::os::impl { TimeSpan min_timeout = 0; WaitableHolderBase *min_timeout_object = this->RecalculateNextTimeout(&min_timeout, end_time); - s32 index; - if (infinite && min_timeout_object == nullptr) { - index = this->target_impl.WaitAny(object_handles, MaximumHandleCount, count); + s32 index = WaitInvalid; + Result wait_result = ResultSuccess(); + if (reply) { + if (infinite && min_timeout_object == nullptr) { + + } else { + + } + } else if (infinite && min_timeout_object == nullptr) { + wait_result = this->target_impl.WaitAny(std::addressof(index), object_handles, MaximumHandleCount, count); } else { if (count == 0 && min_timeout == 0) { index = WaitTimedOut; } else { - index = this->target_impl.TimedWaitAny(object_handles, MaximumHandleCount, count, min_timeout); + wait_result = this->target_impl.TimedWaitAny(std::addressof(index), object_handles, MaximumHandleCount, count, min_timeout); AMS_ABORT_UNLESS(index != WaitInvalid); } } + if (index == WaitInvalid) { + *out = nullptr; + return wait_result; + } + switch (index) { case WaitTimedOut: if (min_timeout_object) { @@ -79,23 +103,35 @@ namespace ams::os::impl { if (min_timeout_object->IsSignaled() == TriBool::True) { std::scoped_lock lk(this->cs_wait); this->signaled_holder = min_timeout_object; - return this->signaled_holder; + *out = min_timeout_object; + return wait_result; } - continue; + } else { + *out = nullptr; + return wait_result; } - return nullptr; + break; case WaitCancelled: - if (this->signaled_holder) { - return this->signaled_holder; - } - continue; - default: /* 0 - 0x3F, valid. */ { + std::scoped_lock lk(this->cs_wait); + if (this->signaled_holder) { + *out = this->signaled_holder; + return wait_result; + } + } + break; + default: /* 0 - 0x3F, valid. */ + { + AMS_ASSERT(0 <= index && index < static_cast(MaximumHandleCount)); + std::scoped_lock lk(this->cs_wait); this->signaled_holder = objects[index]; - return this->signaled_holder; + *out = objects[index]; + return wait_result; } } + + reply_target = svc::InvalidHandle; } } diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp index a014c4ff1..e74ed53e6 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_impl.hpp @@ -38,14 +38,21 @@ namespace ams::os::impl { InternalCriticalSection cs_wait; WaitableManagerTargetImpl target_impl; private: - WaitableHolderBase *WaitAnyImpl(bool infinite, TimeSpan timeout); - WaitableHolderBase *WaitAnyHandleImpl(bool infinite, TimeSpan timeout); + Result WaitAnyImpl(WaitableHolderBase **out, bool infinite, TimeSpan timeout, bool reply, Handle reply_target); + Result WaitAnyHandleImpl(WaitableHolderBase **out, bool infinite, TimeSpan timeout, bool reply, Handle reply_target); s32 BuildHandleArray(Handle out_handles[], WaitableHolderBase *out_objects[], s32 num); WaitableHolderBase *LinkHoldersToObjectList(); void UnlinkHoldersFromObjectList(); WaitableHolderBase *RecalculateNextTimeout(TimeSpan *out_min_timeout, TimeSpan end_time); + + WaitableHolderBase *WaitAnyImpl(bool infinite, TimeSpan timeout) { + WaitableHolderBase *holder = nullptr; + const Result wait_result = this->WaitAnyImpl(std::addressof(holder), infinite, timeout, false, svc::InvalidHandle); + AMS_ASSERT(R_SUCCEEDED(wait_result)); + return holder; + } public: /* Wait. */ WaitableHolderBase *WaitAny() { @@ -60,6 +67,10 @@ namespace ams::os::impl { return this->WaitAnyImpl(false, ts); } + Result ReplyAndReceive(WaitableHolderBase **out, Handle reply_target) { + return this->WaitAnyImpl(out, true, TimeSpan::FromNanoSeconds(std::numeric_limits::max()), true, reply_target); + } + /* List management. */ bool IsEmpty() const { return this->waitable_list.empty(); diff --git a/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp b/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp index 2f4b95131..784daae4d 100644 --- a/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp +++ b/libraries/libstratosphere/source/os/impl/os_waitable_manager_target_impl.os.horizon.cpp @@ -19,13 +19,13 @@ namespace ams::os::impl { - s32 WaitableManagerHorizonImpl::WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns) { + Result WaitableManagerHorizonImpl::WaitSynchronizationN(s32 *out_index, s32 num, Handle arr[], s32 array_size, s64 ns) { AMS_ASSERT(!(num == 0 && ns == 0)); s32 index = WaitableManagerImpl::WaitInvalid; R_TRY_CATCH(svc::WaitSynchronization(std::addressof(index), static_cast(arr), num, ns)) { - R_CATCH(svc::ResultTimedOut) { return WaitableManagerImpl::WaitTimedOut; } - R_CATCH(svc::ResultCancelled) { return WaitableManagerImpl::WaitCancelled; } + R_CATCH(svc::ResultTimedOut) { index = WaitableManagerImpl::WaitTimedOut; } + R_CATCH(svc::ResultCancelled) { index = WaitableManagerImpl::WaitCancelled; } /* All other results are critical errors. */ /* svc::ResultThreadTerminating */ /* svc::ResultInvalidHandle. */ @@ -33,7 +33,34 @@ namespace ams::os::impl { /* svc::ResultOutOfRange */ } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - return index; + *out_index = index; + return ResultSuccess(); + } + + Result ReplyAndReceiveN(s32 *out_index, s32 num, Handle arr[], s32 array_size, s64 ns, Handle reply_target) { + /* NOTE: Nintendo does not initialize this value, which seems like it can cause incorrect behavior. */ + s32 index = WaitableManagerImpl::WaitInvalid; + static_assert(WaitableManagerImpl::WaitInvalid != -1); + + R_TRY_CATCH(svc::ReplyAndReceive(std::addressof(index), arr, num, ns, reply_target)) { + R_CATCH(svc::ResultTimedOut) { *out_index = WaitableManagerImpl::WaitTimedOut; return R_CURRENT_RESULT; } + R_CATCH(svc::ResultCancelled) { *out_index = WaitableManagerImpl::WaitCancelled; return R_CURRENT_RESULT; } + R_CATCH(svc::ResultSessionClosed) { + if (index == -1) { + *out_index = WaitableManagerImpl::WaitInvalid; + return os::ResultSessionClosedForReply(); + } else { + *out_index = index; + return os::ResultSessionClosedForReceive(); + } + } + R_CATCH(svc::ResultReceiveListBroken) { + *out_index = index; + return os::ResultReceiveListBroken(); + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return ResultSuccess(); } void WaitableManagerHorizonImpl::CancelWait() { 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 46c13d3b7..ac7ee6b92 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 @@ -25,24 +25,33 @@ namespace ams::os::impl { private: Handle handle; private: - s32 WaitSynchronizationN(s32 num, Handle arr[], s32 array_size, s64 ns); + Result WaitSynchronizationN(s32 *out_index, s32 num, Handle arr[], s32 array_size, s64 ns); + Result ReplyAndReceiveN(s32 *out_index, s32 num, Handle arr[], s32 array_size, s64 ns, Handle reply_target); public: void CancelWait(); - s32 WaitAny(Handle arr[], s32 array_size, s32 num) { - return this->WaitSynchronizationN(num, arr, array_size, svc::WaitInfinite); + Result WaitAny(s32 *out_index, Handle arr[], s32 array_size, s32 num) { + return this->WaitSynchronizationN(out_index, num, arr, array_size, svc::WaitInfinite); } - s32 TryWaitAny(Handle arr[], s32 array_size, s32 num) { - return this->WaitSynchronizationN(num, arr, array_size, 0); + Result TryWaitAny(s32 *out_index, Handle arr[], s32 array_size, s32 num) { + return this->WaitSynchronizationN(out_index, num, arr, array_size, 0); } - s32 TimedWaitAny(Handle arr[], s32 array_size, s32 num, TimeSpan ts) { + Result TimedWaitAny(s32 *out_index, Handle arr[], s32 array_size, s32 num, TimeSpan ts) { s64 timeout = ts.GetNanoSeconds(); if (timeout < 0) { timeout = 0; } - return this->WaitSynchronizationN(num, arr, array_size, timeout); + return this->WaitSynchronizationN(out_index, num, arr, array_size, timeout); + } + + Result ReplyAndReceive(s32 *out_index, Handle arr[], s32 array_size, s32 num, Handle reply_target) { + return this->ReplyAndReceiveN(out_index, num, arr, array_size, std::numeric_limits::max(), reply_target); + } + + Result TimedReplyAndReceive(s32 *out_index, Handle arr[], s32 array_size, s32 num, Handle reply_target, TimeSpan ts) { + return this->ReplyAndReceiveN(out_index, num, arr, array_size, ts.GetNanoSeconds(), reply_target); } void SetCurrentThreadHandleForCancelWait() { diff --git a/libraries/libstratosphere/source/os/os_sdk_reply_and_receive.cpp b/libraries/libstratosphere/source/os/os_sdk_reply_and_receive.cpp new file mode 100644 index 000000000..a0242cd66 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_sdk_reply_and_receive.cpp @@ -0,0 +1,47 @@ +/* + * 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 . + */ +#include +#include "impl/os_waitable_manager_impl.hpp" +#include "impl/os_waitable_holder_base.hpp" +#include "impl/os_waitable_holder_impl.hpp" + +namespace ams::os { + + namespace { + + ALWAYS_INLINE impl::WaitableManagerImpl &GetWaitableManagerImpl(WaitableManagerType *manager) { + return GetReference(manager->impl_storage); + } + + ALWAYS_INLINE WaitableHolderType *CastToWaitableHolder(impl::WaitableHolderBase *base) { + return reinterpret_cast(base); + } + + } + + Result SdkReplyAndReceive(os::WaitableHolderType **out, Handle reply_target, WaitableManagerType *manager) { + auto &impl = GetWaitableManagerImpl(manager); + + AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); + AMS_ASSERT(!impl.IsEmpty()); + + impl::WaitableHolderBase *holder_base; + ON_SCOPE_EXIT { *out = CastToWaitableHolder(holder_base); }; + + return impl.ReplyAndReceive(std::addressof(holder_base), reply_target); + } + +} diff --git a/libraries/libstratosphere/source/os/os_waitable.cpp b/libraries/libstratosphere/source/os/os_waitable.cpp index 20870881f..5653eaf2c 100644 --- a/libraries/libstratosphere/source/os/os_waitable.cpp +++ b/libraries/libstratosphere/source/os/os_waitable.cpp @@ -26,7 +26,7 @@ namespace ams::os { return GetReference(manager->impl_storage); } - ALWAYS_INLINE WaitableHolderType *ReinterpretAsWaitableHolder(impl::WaitableHolderBase *base) { + ALWAYS_INLINE WaitableHolderType *CastToWaitableHolder(impl::WaitableHolderBase *base) { return reinterpret_cast(base); } @@ -59,7 +59,7 @@ namespace ams::os { AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); AMS_ASSERT(!impl.IsEmpty()); - auto *holder = ReinterpretAsWaitableHolder(impl.WaitAny()); + auto *holder = CastToWaitableHolder(impl.WaitAny()); AMS_ASSERT(holder != nullptr); return holder; } @@ -70,7 +70,7 @@ namespace ams::os { AMS_ASSERT(manager->state == WaitableManagerType::State_Initialized); AMS_ASSERT(!impl.IsEmpty()); - auto *holder = ReinterpretAsWaitableHolder(impl.TryWaitAny()); + auto *holder = CastToWaitableHolder(impl.TryWaitAny()); return holder; } @@ -81,7 +81,7 @@ namespace ams::os { AMS_ASSERT(!impl.IsEmpty()); AMS_ASSERT(timeout.GetNanoSeconds() >= 0); - auto *holder = ReinterpretAsWaitableHolder(impl.TimedWaitAny(timeout)); + auto *holder = CastToWaitableHolder(impl.TimedWaitAny(timeout)); return holder; } diff --git a/libraries/libvapours/include/vapours/results/os_results.hpp b/libraries/libvapours/include/vapours/results/os_results.hpp index 05479e26d..9c300ff31 100644 --- a/libraries/libvapours/include/vapours/results/os_results.hpp +++ b/libraries/libvapours/include/vapours/results/os_results.hpp @@ -37,4 +37,8 @@ namespace ams::os { R_DEFINE_ERROR_RESULT(OutOfTransferMemory, 505); R_DEFINE_ERROR_RESULT(OutOfAddressSpace, 506); + R_DEFINE_ERROR_RESULT(SessionClosedForReceive, 510); + R_DEFINE_ERROR_RESULT(SessionClosedForReply, 511); + R_DEFINE_ERROR_RESULT(ReceiveListBroken, 512); + }