kern: implement SvcWaitSynchronization

This commit is contained in:
Michael Scire 2020-07-09 17:21:47 -07:00
parent 16c9c53a4a
commit f52232f0f2
10 changed files with 211 additions and 59 deletions

View file

@ -211,6 +211,49 @@ namespace ams::kern {
static_assert(std::is_base_of<KAutoObject, T>::value);
return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
}
template<typename T>
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<T*>();
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<typename T>
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<T*>();
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;
}
};
}

View file

@ -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();

View file

@ -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<u16>(this->tls_address + 0x100); }
constexpr void SetKernelPreemptionState(u16 state) const { *GetPointer<u16>(this->tls_address + 0x100 + sizeof(u16)) = state; }

View file

@ -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<KSynchronizationObject::iterator *>(__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) {

View file

@ -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);

View file

@ -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<const ams::svc::Handle *> 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<KSynchronizationObject>(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<const ams::svc::Handle *> handles, int32_t numHandles, int64_t timeout_ns) {
MESOSPHERE_PANIC("Stubbed SvcWaitSynchronization64 was called.");
Result WaitSynchronization64(int32_t *out_index, KUserPointer<const ams::svc::Handle *> 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<const ams::svc::Handle *> handles, int32_t numHandles, int64_t timeout_ns) {
MESOSPHERE_PANIC("Stubbed SvcWaitSynchronization64From32 was called.");
Result WaitSynchronization64From32(int32_t *out_index, KUserPointer<const ams::svc::Handle *> handles, int32_t num_handles, int64_t timeout_ns) {
return WaitSynchronization(out_index, handles, num_handles, timeout_ns);
}
Result CancelSynchronization64From32(ams::svc::Handle handle) {

View file

@ -110,8 +110,8 @@ namespace ams::svc::aarch64::lp64 {
return ::svcResetSignal(handle);
}
ALWAYS_INLINE Result WaitSynchronization(int32_t *out_index, ::ams::svc::UserPointer<const ::ams::svc::Handle *> 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<const ::ams::svc::Handle *> 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) {

View file

@ -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<size_t>(ArgumentHandleCountMax);
private:
Handle handle;
private:

View file

@ -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;

View file

@ -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)) \