From 4b03e6e776e6421c2b2c290b0822b9e5a8556a4c Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 24 Apr 2021 02:40:31 -0700 Subject: [PATCH] hle: kernel: Migrate to KHandleTable. --- src/core/CMakeLists.txt | 4 +- src/core/hle/kernel/handle_table.cpp | 125 ------- src/core/hle/kernel/handle_table.h | 212 ------------ src/core/hle/kernel/hle_ipc.cpp | 6 +- src/core/hle/kernel/hle_ipc.h | 7 +- src/core/hle/kernel/k_auto_object.h | 2 - src/core/hle/kernel/k_condition_variable.cpp | 2 +- src/core/hle/kernel/k_handle_table.cpp | 135 ++++++++ src/core/hle/kernel/k_handle_table.h | 309 ++++++++++++++++++ src/core/hle/kernel/k_process.cpp | 2 +- src/core/hle/kernel/k_process.h | 8 +- .../k_scoped_scheduler_lock_and_sleep.h | 4 +- src/core/hle/kernel/k_server_session.cpp | 2 +- src/core/hle/kernel/k_thread.cpp | 2 +- src/core/hle/kernel/kernel.cpp | 20 +- src/core/hle/kernel/kernel.h | 7 +- src/core/hle/kernel/process_capability.cpp | 4 +- src/core/hle/kernel/svc.cpp | 8 +- src/core/hle/kernel/svc_common.h | 15 + src/core/hle/kernel/time_manager.cpp | 1 - src/yuzu/debugger/wait_tree.cpp | 4 +- src/yuzu/debugger/wait_tree.h | 5 +- 22 files changed, 503 insertions(+), 381 deletions(-) delete mode 100644 src/core/hle/kernel/handle_table.cpp delete mode 100644 src/core/hle/kernel/handle_table.h create mode 100644 src/core/hle/kernel/k_handle_table.cpp create mode 100644 src/core/hle/kernel/k_handle_table.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 889a2d2f80..83da304182 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -149,8 +149,6 @@ add_library(core STATIC hle/kernel/svc_results.h hle/kernel/global_scheduler_context.cpp hle/kernel/global_scheduler_context.h - hle/kernel/handle_table.cpp - hle/kernel/handle_table.h hle/kernel/hle_ipc.cpp hle/kernel/hle_ipc.h hle/kernel/init/init_slab_setup.cpp @@ -174,6 +172,8 @@ add_library(core STATIC hle/kernel/k_condition_variable.h hle/kernel/k_event.cpp hle/kernel/k_event.h + hle/kernel/k_handle_table.cpp + hle/kernel/k_handle_table.h hle/kernel/k_light_condition_variable.h hle/kernel/k_light_lock.cpp hle/kernel/k_light_lock.h diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp deleted file mode 100644 index 16c528f5b5..0000000000 --- a/src/core/hle/kernel/handle_table.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/core.h" -#include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/k_process.h" -#include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/k_thread.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/svc_results.h" - -namespace Kernel { -namespace { -constexpr u16 GetSlot(Handle handle) { - return static_cast(handle >> 15); -} - -constexpr u16 GetGeneration(Handle handle) { - return static_cast(handle & 0x7FFF); -} -} // Anonymous namespace - -HandleTable::HandleTable(KernelCore& kernel) : kernel{kernel} { - Clear(); -} - -HandleTable::~HandleTable() = default; - -ResultCode HandleTable::SetSize(s32 handle_table_size) { - if (static_cast(handle_table_size) > MAX_COUNT) { - LOG_ERROR(Kernel, "Handle table size {} is greater than {}", handle_table_size, MAX_COUNT); - return ResultOutOfMemory; - } - - // Values less than or equal to zero indicate to use the maximum allowable - // size for the handle table in the actual kernel, so we ignore the given - // value in that case, since we assume this by default unless this function - // is called. - if (handle_table_size > 0) { - table_size = static_cast(handle_table_size); - } - - return RESULT_SUCCESS; -} - -ResultCode HandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { - ASSERT(obj != nullptr); - - const u16 slot = next_free_slot; - if (slot >= table_size) { - LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); - return ResultOutOfHandles; - } - next_free_slot = generations[slot]; - - const u16 generation = next_generation++; - - // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. - // Horizon OS uses zero to represent an invalid handle, so skip to 1. - if (next_generation >= (1 << 15)) { - next_generation = 1; - } - - generations[slot] = generation; - objects[slot] = obj; - obj->Open(); - - *out_handle = generation | (slot << 15); - - return RESULT_SUCCESS; -} - -ResultVal HandleTable::Duplicate(Handle handle) { - auto object = GetObject(handle); - if (object.IsNull()) { - LOG_ERROR(Kernel, "Tried to duplicate invalid handle: {:08X}", handle); - return ResultInvalidHandle; - } - - Handle out_handle{}; - R_TRY(Add(&out_handle, object.GetPointerUnsafe())); - - return MakeResult(out_handle); -} - -bool HandleTable::Remove(Handle handle) { - if (!IsValid(handle)) { - LOG_ERROR(Kernel, "Handle is not valid! handle={:08X}", handle); - return {}; - } - - const u16 slot = GetSlot(handle); - - if (objects[slot]) { - objects[slot]->Close(); - } - - objects[slot] = nullptr; - - generations[slot] = next_free_slot; - next_free_slot = slot; - - return true; -} - -bool HandleTable::IsValid(Handle handle) const { - const std::size_t slot = GetSlot(handle); - const u16 generation = GetGeneration(handle); - const bool is_object_valid = (objects[slot] != nullptr); - return slot < table_size && is_object_valid && generations[slot] == generation; -} - -void HandleTable::Clear() { - for (u16 i = 0; i < table_size; ++i) { - generations[i] = static_cast(i + 1); - objects[i] = nullptr; - } - next_free_slot = 0; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h deleted file mode 100644 index 791e303d10..0000000000 --- a/src/core/hle/kernel/handle_table.h +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include - -#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/result.h" - -namespace Kernel { - -class KernelCore; - -enum KernelHandle : Handle { - InvalidHandle = 0, - CurrentThread = 0xFFFF8000, - CurrentProcess = 0xFFFF8001, -}; - -/** - * This class allows the creation of Handles, which are references to objects that can be tested - * for validity and looked up. Here they are used to pass references to kernel objects to/from the - * emulated process. it has been designed so that it follows the same handle format and has - * approximately the same restrictions as the handle manager in the CTR-OS. - * - * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). - * The slot index is used to index into the arrays in this class to access the data corresponding - * to the Handle. - * - * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter - * is kept and incremented every time a Handle is created. This is the Handle's "generation". The - * value of the counter is stored into the Handle as well as in the handle table (in the - * "generations" array). When looking up a handle, the Handle's generation must match with the - * value stored on the class, otherwise the Handle is considered invalid. - * - * To find free slots when allocating a Handle without needing to scan the entire object array, the - * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. - * When a Handle is created, an index is popped off the list and used for the new Handle. When it - * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is - * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been - * verified and isn't likely to cause any problems. - */ -class HandleTable final : NonCopyable { -public: - /// This is the maximum limit of handles allowed per process in Horizon - static constexpr std::size_t MAX_COUNT = 1024; - - explicit HandleTable(KernelCore& kernel); - ~HandleTable(); - - /** - * Sets the number of handles that may be in use at one time - * for this handle table. - * - * @param handle_table_size The desired size to limit the handle table to. - * - * @returns an error code indicating if initialization was successful. - * If initialization was not successful, then ERR_OUT_OF_MEMORY - * will be returned. - * - * @pre handle_table_size must be within the range [0, 1024] - */ - ResultCode SetSize(s32 handle_table_size); - - /** - * Returns a new handle that points to the same object as the passed in handle. - * @return The duplicated Handle or one of the following errors: - * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. - * - Any errors returned by `Create()`. - */ - ResultVal Duplicate(Handle handle); - - /** - * Closes a handle, removing it from the table and decreasing the object's ref-count. - * @return `RESULT_SUCCESS` or one of the following errors: - * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. - */ - bool Remove(Handle handle); - - /// Checks if a handle is valid and points to an existing object. - bool IsValid(Handle handle) const; - - template - KAutoObject* GetObjectImpl(Handle handle) const { - if (!IsValid(handle)) { - return nullptr; - } - - auto* obj = objects[static_cast(handle >> 15)]; - return obj->DynamicCast(); - } - - template - KScopedAutoObject GetObject(Handle handle) const { - if (handle == CurrentThread) { - return kernel.CurrentScheduler()->GetCurrentThread()->DynamicCast(); - } else if (handle == CurrentProcess) { - return kernel.CurrentProcess()->DynamicCast(); - } - - if (!IsValid(handle)) { - return nullptr; - } - - auto* obj = objects[static_cast(handle >> 15)]; - return obj->DynamicCast(); - } - - template - KScopedAutoObject GetObjectWithoutPseudoHandle(Handle handle) const { - if (!IsValid(handle)) { - return nullptr; - } - auto* obj = objects[static_cast(handle >> 15)]; - return obj->DynamicCast(); - } - - /// Closes all handles held in this table. - void Clear(); - - // NEW IMPL - - template - ResultCode Add(Handle* out_handle, T* obj) { - static_assert(std::is_base_of::value); - return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken()); - } - - ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type); - - template - 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(); - 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 objects{}; - - /** - * The value of `next_generation` when the handle was created, used to check for validity. For - * empty slots, contains the index of the next free slot in the list. - */ - std::array generations; - - /** - * The limited size of the handle table. This can be specified by process - * capabilities in order to restrict the overall number of handles that - * can be created in a process instance - */ - u16 table_size = static_cast(MAX_COUNT); - - /** - * Global counter of the number of created handles. Stored in `generations` when a handle is - * created, and wraps around to 1 when it hits 0x8000. - */ - u16 next_generation = 1; - - /// 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; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 69190286d2..b505d20a6e 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -14,8 +14,8 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_scheduler.h" @@ -50,7 +50,7 @@ HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& HLERequestContext::~HLERequestContext() = default; -void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, +void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf, bool incoming) { IPC::RequestParser rp(src_cmdbuf); command_header = rp.PopRaw(); @@ -163,7 +163,7 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_ rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. } -ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, +ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf) { ParseCommandBuffer(handle_table, src_cmdbuf, true); if (command_header->type == IPC::CommandType::Close) { diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 4b92ba6558..fa031c1212 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -17,6 +17,7 @@ #include "common/swap.h" #include "core/hle/ipc.h" #include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/svc_common.h" union ResultCode; @@ -35,9 +36,9 @@ class ServiceFrameworkBase; namespace Kernel { class Domain; -class HandleTable; class HLERequestContext; class KernelCore; +class KHandleTable; class KProcess; class KServerSession; class KThread; @@ -121,7 +122,7 @@ public: } /// Populates this context with data from the requesting process/thread. - ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, + ResultCode PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf); /// Writes data from this context back to the requesting process/thread. @@ -267,7 +268,7 @@ public: private: friend class IPC::ResponseBuilder; - void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); + void ParseCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); std::array cmd_buf; Kernel::KServerSession* server_session{}; diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index 5a180b7dcb..32aaf9fc50 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h @@ -17,8 +17,6 @@ namespace Kernel { class KernelCore; class KProcess; -using Handle = u32; - #define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \ NON_COPYABLE(CLASS); \ NON_MOVEABLE(CLASS); \ diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index a9738f7ced..f51cf3e7bf 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -179,7 +179,7 @@ KThread* KConditionVariable::SignalImpl(KThread* thread) { KThread* thread_to_close = nullptr; if (can_access) { - if (prev_tag == InvalidHandle) { + if (prev_tag == Svc::InvalidHandle) { // If nobody held the lock previously, we're all good. thread->SetSyncedObject(nullptr, RESULT_SUCCESS); thread->Wakeup(); diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp new file mode 100644 index 0000000000..0378447f68 --- /dev/null +++ b/src/core/hle/kernel/k_handle_table.cpp @@ -0,0 +1,135 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/kernel/k_handle_table.h" + +namespace Kernel { + +KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {} +KHandleTable ::~KHandleTable() = default; + +ResultCode KHandleTable::Finalize() { + // Get the table and clear our record of it. + u16 saved_table_size = 0; + { + KScopedSpinLock lk(m_lock); + + std::swap(m_table_size, saved_table_size); + } + + // Close and free all entries. + for (size_t i = 0; i < saved_table_size; i++) { + if (KAutoObject* obj = m_objects[i]; obj != nullptr) { + obj->Close(); + } + } + + return RESULT_SUCCESS; +} + +bool KHandleTable::Remove(Handle handle) { + // Don't allow removal of a pseudo-handle. + if (Svc::IsPseudoHandle(handle)) { + return false; + } + + // Handles must not have reserved bits set. + const auto handle_pack = HandlePack(handle); + if (handle_pack.reserved != 0) { + return false; + } + + // Find the object and free the entry. + KAutoObject* obj = nullptr; + { + KScopedSpinLock lk(m_lock); + + if (this->IsValidHandle(handle)) { + const auto index = handle_pack.index; + + obj = m_objects[index]; + this->FreeEntry(index); + } else { + return false; + } + } + + // Close the object. + obj->Close(); + return true; +} + +ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { + KScopedSpinLock lk(m_lock); + + // Never exceed our capacity. + R_UNLESS(m_count < m_table_size, ResultOutOfHandles); + + // Allocate entry, set output handle. + { + const auto linear_id = this->AllocateLinearId(); + const auto index = this->AllocateEntry(); + + m_entry_infos[index].info = {.linear_id = linear_id, .type = type}; + m_objects[index] = obj; + + obj->Open(); + + *out_handle = EncodeHandle(static_cast(index), linear_id); + } + + return RESULT_SUCCESS; +} + +ResultCode KHandleTable::Reserve(Handle* out_handle) { + KScopedSpinLock lk(m_lock); + + // Never exceed our capacity. + R_UNLESS(m_count < m_table_size, ResultOutOfHandles); + + *out_handle = EncodeHandle(static_cast(this->AllocateEntry()), this->AllocateLinearId()); + return RESULT_SUCCESS; +} + +void KHandleTable::Unreserve(Handle handle) { + KScopedSpinLock lk(m_lock); + + // Unpack the handle. + const auto handle_pack = HandlePack(handle); + const auto index = handle_pack.index; + const auto linear_id = handle_pack.linear_id; + const auto reserved = handle_pack.reserved; + ASSERT(reserved == 0); + ASSERT(linear_id != 0); + + if (index < m_table_size) { + // NOTE: This code does not check the linear id. + ASSERT(m_objects[index] == nullptr); + this->FreeEntry(index); + } +} + +void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { + KScopedSpinLock lk(m_lock); + + // Unpack the handle. + const auto handle_pack = HandlePack(handle); + const auto index = handle_pack.index; + const auto linear_id = handle_pack.linear_id; + const auto reserved = handle_pack.reserved; + ASSERT(reserved == 0); + ASSERT(linear_id != 0); + + if (index < m_table_size) { + // Set the entry. + ASSERT(m_objects[index] == nullptr); + + m_entry_infos[index].info = {.linear_id = static_cast(linear_id), .type = type}; + m_objects[index] = obj; + + obj->Open(); + } +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h new file mode 100644 index 0000000000..e38ad0fd94 --- /dev/null +++ b/src/core/hle/kernel/k_handle_table.h @@ -0,0 +1,309 @@ +// Copyright 2021 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/assert.h" +#include "common/bit_field.h" +#include "common/bit_util.h" +#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/k_thread.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/svc_common.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/result.h" + +namespace Kernel { + +class KernelCore; + +class KHandleTable { + NON_COPYABLE(KHandleTable); + NON_MOVEABLE(KHandleTable); + +public: + static constexpr size_t MaxTableSize = 1024; + +private: + union HandlePack { + u32 raw; + BitField<0, 15, u32> index; + BitField<15, 15, u32> linear_id; + BitField<30, 2, u32> reserved; + }; + + static constexpr u16 MinLinearId = 1; + static constexpr u16 MaxLinearId = 0x7FFF; + + static constexpr Handle EncodeHandle(u16 index, u16 linear_id) { + HandlePack handle{}; + handle.index.Assign(index); + handle.linear_id.Assign(linear_id); + handle.reserved.Assign(0); + return handle.raw; + } + + union EntryInfo { + struct { + u16 linear_id; + u16 type; + } info; + s32 next_free_index; + + constexpr u16 GetLinearId() const { + return info.linear_id; + } + constexpr u16 GetType() const { + return info.type; + } + constexpr s32 GetNextFreeIndex() const { + return next_free_index; + } + }; + +private: + std::array m_entry_infos{}; + std::array m_objects{}; + s32 m_free_head_index{-1}; + u16 m_table_size{}; + u16 m_max_count{}; + u16 m_next_linear_id{MinLinearId}; + u16 m_count{}; + mutable KSpinLock m_lock; + +public: + explicit KHandleTable(KernelCore& kernel_); + ~KHandleTable(); + + constexpr ResultCode Initialize(s32 size) { + R_UNLESS(size <= static_cast(MaxTableSize), ResultOutOfMemory); + + // Initialize all fields. + m_max_count = 0; + m_table_size = static_cast((size <= 0) ? MaxTableSize : size); + m_next_linear_id = MinLinearId; + m_count = 0; + m_free_head_index = -1; + + // Free all entries. + for (s32 i = 0; i < static_cast(m_table_size); ++i) { + m_objects[i] = nullptr; + m_entry_infos[i].next_free_index = i - 1; + m_free_head_index = i; + } + + return RESULT_SUCCESS; + } + + constexpr size_t GetTableSize() const { + return m_table_size; + } + constexpr size_t GetCount() const { + return m_count; + } + constexpr size_t GetMaxCount() const { + return m_max_count; + } + + ResultCode Finalize(); + bool Remove(Handle handle); + + template + KScopedAutoObject GetObjectWithoutPseudoHandle(Handle handle) const { + // Lock and look up in table. + KScopedSpinLock lk(m_lock); + + if constexpr (std::is_same::value) { + return this->GetObjectImpl(handle); + } else { + if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) { + return obj->DynamicCast(); + } else { + return nullptr; + } + } + } + + template + KScopedAutoObject GetObject(Handle handle) const { + // Handle pseudo-handles. + if constexpr (std::derived_from) { + if (handle == Svc::PseudoHandle::CurrentProcess) { + auto* const cur_process = kernel.CurrentProcess(); + ASSERT(cur_process != nullptr); + return cur_process; + } + } else if constexpr (std::derived_from) { + if (handle == Svc::PseudoHandle::CurrentThread) { + auto* const cur_thread = GetCurrentThreadPointer(kernel); + ASSERT(cur_thread != nullptr); + return cur_thread; + } + } + + return this->template GetObjectWithoutPseudoHandle(handle); + } + + ResultCode Reserve(Handle* out_handle); + void Unreserve(Handle handle); + + template + ResultCode Add(Handle* out_handle, T* obj) { + static_assert(std::is_base_of::value); + return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken()); + } + + template + void Register(Handle handle, T* obj) { + static_assert(std::is_base_of::value); + return this->Register(handle, obj, obj->GetTypeObj().GetClassToken()); + } + + template + 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(m_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(); + 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: + ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type); + void Register(Handle handle, KAutoObject* obj, u16 type); + + constexpr s32 AllocateEntry() { + ASSERT(m_count < m_table_size); + + const auto index = m_free_head_index; + + m_free_head_index = m_entry_infos[index].GetNextFreeIndex(); + + m_max_count = std::max(m_max_count, ++m_count); + + return index; + } + + constexpr void FreeEntry(s32 index) { + ASSERT(m_count > 0); + + m_objects[index] = nullptr; + m_entry_infos[index].next_free_index = m_free_head_index; + + m_free_head_index = index; + + --m_count; + } + + constexpr u16 AllocateLinearId() { + const u16 id = m_next_linear_id++; + if (m_next_linear_id > MaxLinearId) { + m_next_linear_id = MinLinearId; + } + return id; + } + + constexpr bool IsValidHandle(Handle handle) const { + // Unpack the handle. + const auto handle_pack = HandlePack(handle); + const auto raw_value = handle_pack.raw; + const auto index = handle_pack.index; + const auto linear_id = handle_pack.linear_id; + const auto reserved = handle_pack.reserved; + ASSERT(reserved == 0); + + // Validate our indexing information. + if (raw_value == 0) { + return false; + } + if (linear_id == 0) { + return false; + } + if (index >= m_table_size) { + return false; + } + + // Check that there's an object, and our serial id is correct. + if (m_objects[index] == nullptr) { + return false; + } + if (m_entry_infos[index].GetLinearId() != linear_id) { + return false; + } + + return true; + } + + constexpr KAutoObject* GetObjectImpl(Handle handle) const { + // Handles must not have reserved bits set. + const auto handle_pack = HandlePack(handle); + if (handle_pack.reserved != 0) { + return nullptr; + } + + if (this->IsValidHandle(handle)) { + return m_objects[handle_pack.index]; + } else { + return nullptr; + } + } + + constexpr KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const { + + // Index must be in bounds. + if (index >= m_table_size) { + return nullptr; + } + + // Ensure entry has an object. + if (KAutoObject* obj = m_objects[index]; obj != nullptr) { + *out_handle = EncodeHandle(static_cast(index), m_entry_infos[index].GetLinearId()); + return obj; + } else { + return nullptr; + } + } + +private: + KernelCore& kernel; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index e542b1f078..1743181809 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -354,7 +354,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, tls_region_address = CreateTLSRegion(); memory_reservation.Commit(); - return handle_table.SetSize(capabilities.GetHandleTableSize()); + return handle_table.Initialize(capabilities.GetHandleTableSize()); } void KProcess::Run(s32 main_thread_priority, u64 stack_size) { diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 5c54c63609..62ab26b052 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -11,10 +11,10 @@ #include #include #include "common/common_types.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_condition_variable.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/slab_helpers.h" @@ -104,12 +104,12 @@ public: } /// Gets a reference to the process' handle table. - HandleTable& GetHandleTable() { + KHandleTable& GetHandleTable() { return handle_table; } /// Gets a const reference to the process' handle table. - const HandleTable& GetHandleTable() const { + const KHandleTable& GetHandleTable() const { return handle_table; } @@ -429,7 +429,7 @@ private: u64 total_process_running_time_ticks = 0; /// Per-process handle table for storing created object handles in. - HandleTable handle_table; + KHandleTable handle_table; /// Per-process address arbiter. KAddressArbiter address_arbiter; diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index ebecf0c778..1bfbbcfe26 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h @@ -8,7 +8,7 @@ #pragma once #include "common/common_types.h" -#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/time_manager.h" @@ -17,7 +17,7 @@ namespace Kernel { class [[nodiscard]] KScopedSchedulerLockAndSleep { public: - explicit KScopedSchedulerLockAndSleep(KernelCore & kernel, KThread * t, s64 timeout) + explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout) : kernel(kernel), thread(t), timeout_tick(timeout) { // Lock the scheduler. kernel.GlobalSchedulerContext().scheduler_lock.Lock(); diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 3bc259693e..c8acaa4531 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -10,9 +10,9 @@ #include "common/logging/log.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_client_port.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_server_session.h" diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 3de0157ace..ef6dfeeca1 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -18,8 +18,8 @@ #include "core/core.h" #include "core/cpu_manager.h" #include "core/hardware_properties.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_condition_variable.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_resource_limit.h" diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index f64e070818..825fab694e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -26,9 +26,9 @@ #include "core/cpu_manager.h" #include "core/device_memory.h" #include "core/hardware_properties.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/init/init_slab_setup.h" #include "core/hle/kernel/k_client_port.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" #include "core/hle/kernel/k_process.h" @@ -52,8 +52,7 @@ namespace Kernel { struct KernelCore::Impl { explicit Impl(Core::System& system, KernelCore& kernel) - : time_manager{system}, global_handle_table{kernel}, - object_list_container{kernel}, system{system} {} + : time_manager{system}, object_list_container{kernel}, system{system} {} void SetMulticore(bool is_multicore) { this->is_multicore = is_multicore; @@ -61,6 +60,7 @@ struct KernelCore::Impl { void Initialize(KernelCore& kernel) { global_scheduler_context = std::make_unique(kernel); + global_handle_table = std::make_unique(kernel); service_thread_manager = std::make_unique(1, "yuzu:ServiceThreadManager"); @@ -118,7 +118,7 @@ struct KernelCore::Impl { current_process = nullptr; } - global_handle_table.Clear(); + global_handle_table.reset(); preemption_event = nullptr; @@ -648,7 +648,7 @@ struct KernelCore::Impl { // This is the kernel's handle table or supervisor handle table which // stores all the objects in place. - HandleTable global_handle_table; + std::unique_ptr global_handle_table; KAutoObjectWithListContainer object_list_container; @@ -722,7 +722,7 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() { } KScopedAutoObject KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const { - return impl->global_handle_table.GetObject(handle); + return impl->global_handle_table->GetObject(handle); } void KernelCore::AppendNewProcess(KProcess* process) { @@ -876,12 +876,12 @@ u64 KernelCore::CreateNewUserProcessID() { return impl->next_user_process_id++; } -Kernel::HandleTable& KernelCore::GlobalHandleTable() { - return impl->global_handle_table; +KHandleTable& KernelCore::GlobalHandleTable() { + return *impl->global_handle_table; } -const Kernel::HandleTable& KernelCore::GlobalHandleTable() const { - return impl->global_handle_table; +const KHandleTable& KernelCore::GlobalHandleTable() const { + return *impl->global_handle_table; } void KernelCore::RegisterCoreThread(std::size_t core_id) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 0dd9deaeb7..7c46aa9975 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -14,6 +14,7 @@ #include "core/hle/kernel/k_auto_object.h" #include "core/hle/kernel/k_slab_heap.h" #include "core/hle/kernel/memory_types.h" +#include "core/hle/kernel/svc_common.h" namespace Core { class CPUInterruptHandler; @@ -30,10 +31,10 @@ namespace Kernel { class KClientPort; class GlobalSchedulerContext; -class HandleTable; class KAutoObjectWithListContainer; class KClientSession; class KEvent; +class KHandleTable; class KLinkedListNode; class KMemoryManager; class KPort; @@ -308,10 +309,10 @@ private: u64 CreateNewThreadID(); /// Provides a reference to the global handle table. - Kernel::HandleTable& GlobalHandleTable(); + KHandleTable& GlobalHandleTable(); /// Provides a const reference to the global handle table. - const Kernel::HandleTable& GlobalHandleTable() const; + const KHandleTable& GlobalHandleTable() const; struct Impl; std::unique_ptr impl; diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 4ccac0b064..fcb8b1ea5c 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -6,7 +6,7 @@ #include "common/bit_util.h" #include "common/logging/log.h" -#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/svc_results.h" @@ -99,7 +99,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() { interrupt_capabilities.set(); // Allow using the maximum possible amount of handles - handle_table_size = static_cast(HandleTable::MAX_COUNT); + handle_table_size = static_cast(KHandleTable::MaxTableSize); // Allow all debugging capabilities. is_debuggable = true; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 156c565b0e..d3293a1fef 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -21,12 +21,12 @@ #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/cpu_manager.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_memory_block.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_page_table.h" @@ -839,10 +839,10 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle } KProcess* const current_process = system.Kernel().CurrentProcess(); - HandleTable& handle_table = current_process->GetHandleTable(); + KHandleTable& handle_table = current_process->GetHandleTable(); const auto resource_limit = current_process->GetResourceLimit(); if (!resource_limit) { - *result = KernelHandle::InvalidHandle; + *result = Svc::InvalidHandle; // Yes, the kernel considers this a successful operation. return RESULT_SUCCESS; } @@ -1993,7 +1993,7 @@ static ResultCode SignalEvent(Core::System& system, Handle event_handle) { LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); // Get the current handle table. - const HandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); // Get the writable event. KScopedAutoObject writable_event = handle_table.GetObject(event_handle); diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h index 4af0495510..8a8f347b58 100644 --- a/src/core/hle/kernel/svc_common.h +++ b/src/core/hle/kernel/svc_common.h @@ -6,9 +6,24 @@ #include "common/common_types.h" +namespace Kernel { +using Handle = u32; +} + namespace Kernel::Svc { constexpr s32 ArgumentHandleCountMax = 0x40; constexpr u32 HandleWaitMask{1u << 30}; +constexpr inline Handle InvalidHandle = Handle(0); + +enum PseudoHandle : Handle { + CurrentThread = 0xFFFF8000, + CurrentProcess = 0xFFFF8001, +}; + +constexpr bool IsPseudoHandle(const Handle& handle) { + return handle == PseudoHandle::CurrentProcess || handle == PseudoHandle::CurrentThread; +} + } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 59ebfc51f0..ae9b4be2fd 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -6,7 +6,6 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/core_timing_util.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index f040b4c08f..bdfda6c54f 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -12,8 +12,8 @@ #include "common/assert.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_class_token.h" +#include "core/hle/kernel/k_handle_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/hle/kernel/k_scheduler.h" @@ -115,7 +115,7 @@ QString WaitTreeText::GetText() const { return text; } -WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) +WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::KHandleTable& handle_table) : mutex_address(mutex_address) { mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); owner_handle = static_cast(mutex_value & Kernel::Svc::HandleWaitMask); diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 3dd4acab01..d450345dfb 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -14,11 +14,12 @@ #include "common/common_types.h" #include "core/hle/kernel/k_auto_object.h" +#include "core/hle/kernel/svc_common.h" class EmuThread; namespace Kernel { -class HandleTable; +class KHandleTable; class KReadableEvent; class KSynchronizationObject; class KThread; @@ -74,7 +75,7 @@ public: class WaitTreeMutexInfo : public WaitTreeExpandableItem { Q_OBJECT public: - explicit WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table); + explicit WaitTreeMutexInfo(VAddr mutex_address, const Kernel::KHandleTable& handle_table); ~WaitTreeMutexInfo() override; QString GetText() const override;