diff options
-rw-r--r-- | src/core/hle/kernel/handle_table.h | 55 | ||||
-rw-r--r-- | src/core/hle/kernel/svc.cpp | 70 |
2 files changed, 78 insertions, 47 deletions
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index d57188844..d6abdcd47 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/k_spin_lock.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" #include "core/hle/result.h" @@ -111,6 +112,16 @@ public: } template <typename T = KAutoObject> + KAutoObject* GetObjectImpl(Handle handle) const { + if (!IsValid(handle)) { + return nullptr; + } + + auto* obj = objects_new[static_cast<u16>(handle >> 15)]; + return obj->DynamicCast<T*>(); + } + + template <typename T = KAutoObject> KScopedAutoObject<T> GetObject(Handle handle) const { if (handle == CurrentThread) { return kernel.CurrentScheduler()->GetCurrentThread()->DynamicCast<T*>(); @@ -148,6 +159,48 @@ public: ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type); + template <typename T> + bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { + // Try to convert and open all the handles. + size_t num_opened; + { + // Lock the table. + KScopedSpinLock lk(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 (cur_object == nullptr) { + break; + } + + // Cast the current object to the desired type. + T* cur_t = cur_object->DynamicCast<T*>(); + if (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 (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: /// Stores the Object referenced by the handle or null if the slot is empty. std::array<std::shared_ptr<Object>, MAX_COUNT> objects; @@ -175,6 +228,8 @@ private: /// Head of the free slots linked list. u16 next_free_slot = 0; + mutable KSpinLock lock; + /// Underlying kernel instance that this handle table operates under. KernelCore& kernel; }; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 790839a4b..9e8184758 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -430,65 +430,41 @@ static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low, /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, - u64 handle_count, s64 nano_seconds) { - LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", - handles_address, handle_count, nano_seconds); + u64 num_handles, s64 nano_seconds) { + LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}", + handles_address, num_handles, nano_seconds); - auto& memory = system.Memory(); - if (!memory.IsValidVirtualAddress(handles_address)) { - LOG_ERROR(Kernel_SVC, - "Handle address is not a valid virtual address, handle_address=0x{:016X}", - handles_address); - return ResultInvalidPointer; - } - - static constexpr u64 MaxHandles = 0x40; - - if (handle_count > MaxHandles) { - LOG_ERROR(Kernel_SVC, "Handle count specified is too large, expected {} but got {}", - MaxHandles, handle_count); - return ResultOutOfRange; - } + // Ensure number of handles is valid. + R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); auto& kernel = system.Kernel(); - std::vector<KSynchronizationObject*> objects(handle_count); + std::vector<KSynchronizationObject*> objs(num_handles); const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + Handle* handles = system.Memory().GetPointer<Handle>(handles_address); - for (u64 i = 0; i < handle_count; ++i) { - const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); + // Copy user handles. + if (num_handles > 0) { + // Convert the handles to objects. + R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, + num_handles), + ResultInvalidHandle); + } - bool succeeded{}; - { - auto object = handle_table.Get<KSynchronizationObject>(handle); - if (object) { - objects[i] = object; - succeeded = true; - } + // Ensure handles are closed when we're done. + SCOPE_EXIT({ + for (u64 i = 0; i < num_handles; ++i) { + objs[i]->Close(); } + }); - // TODO(bunnei): WORKAROUND WHILE WE HAVE TWO HANDLE TABLES - if (!succeeded) { - { - auto object = handle_table.GetObject<KSynchronizationObject>(handle); - - if (object.IsNull()) { - LOG_ERROR(Kernel_SVC, "Object is a nullptr"); - return ResultInvalidHandle; - } - - objects[i] = object.GetPointerUnsafe(); - succeeded = true; - } - } - } - return KSynchronizationObject::Wait(kernel, index, objects.data(), - static_cast<s32>(objects.size()), nano_seconds); + return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()), + nano_seconds); } static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, - s32 handle_count, u32 timeout_high, s32* index) { + s32 num_handles, u32 timeout_high, s32* index) { const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; - return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds); + return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); } /// Resumes a thread waiting on WaitSynchronization |