diff options
22 files changed, 503 insertions, 381 deletions
| diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 889a2d2f8..83da30418 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 16c528f5b..000000000 --- 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 <utility> -#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<u16>(handle >> 15); -} - -constexpr u16 GetGeneration(Handle handle) { -    return static_cast<u16>(handle & 0x7FFF); -} -} // Anonymous namespace - -HandleTable::HandleTable(KernelCore& kernel) : kernel{kernel} { -    Clear(); -} - -HandleTable::~HandleTable() = default; - -ResultCode HandleTable::SetSize(s32 handle_table_size) { -    if (static_cast<u32>(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<u16>(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<Handle> 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<u16>(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 791e303d1..000000000 --- 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 <array> -#include <cstddef> -#include <memory> - -#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<Handle> 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 <typename T = KAutoObject> -    KAutoObject* GetObjectImpl(Handle handle) const { -        if (!IsValid(handle)) { -            return nullptr; -        } - -        auto* obj = objects[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*>(); -        } else if (handle == CurrentProcess) { -            return kernel.CurrentProcess()->DynamicCast<T*>(); -        } - -        if (!IsValid(handle)) { -            return nullptr; -        } - -        auto* obj = objects[static_cast<u16>(handle >> 15)]; -        return obj->DynamicCast<T*>(); -    } - -    template <typename T = KAutoObject> -    KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { -        if (!IsValid(handle)) { -            return nullptr; -        } -        auto* obj = objects[static_cast<u16>(handle >> 15)]; -        return obj->DynamicCast<T*>(); -    } - -    /// Closes all handles held in this table. -    void Clear(); - -    // NEW IMPL - -    template <typename T> -    ResultCode Add(Handle* out_handle, T* obj) { -        static_assert(std::is_base_of<KAutoObject, T>::value); -        return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken()); -    } - -    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<KAutoObject*, MAX_COUNT> 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<u16, MAX_COUNT> 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<u16>(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 69190286d..b505d20a6 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<IPC::CommandHeader>(); @@ -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 4b92ba655..fa031c121 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<u32, IPC::COMMAND_BUFFER_LENGTH> 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 5a180b7dc..32aaf9fc5 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 a9738f7ce..f51cf3e7b 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 000000000..0378447f6 --- /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<u16>(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<u16>(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<u16>(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 000000000..e38ad0fd9 --- /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 <array> + +#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<EntryInfo, MaxTableSize> m_entry_infos{}; +    std::array<KAutoObject*, MaxTableSize> 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<s32>(MaxTableSize), ResultOutOfMemory); + +        // Initialize all fields. +        m_max_count = 0; +        m_table_size = static_cast<u16>((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<s32>(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 <typename T = KAutoObject> +    KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { +        // Lock and look up in table. +        KScopedSpinLock lk(m_lock); + +        if constexpr (std::is_same<T, KAutoObject>::value) { +            return this->GetObjectImpl(handle); +        } else { +            if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) { +                return obj->DynamicCast<T*>(); +            } else { +                return nullptr; +            } +        } +    } + +    template <typename T = KAutoObject> +    KScopedAutoObject<T> GetObject(Handle handle) const { +        // Handle pseudo-handles. +        if constexpr (std::derived_from<KProcess, T>) { +            if (handle == Svc::PseudoHandle::CurrentProcess) { +                auto* const cur_process = kernel.CurrentProcess(); +                ASSERT(cur_process != nullptr); +                return cur_process; +            } +        } else if constexpr (std::derived_from<KThread, T>) { +            if (handle == Svc::PseudoHandle::CurrentThread) { +                auto* const cur_thread = GetCurrentThreadPointer(kernel); +                ASSERT(cur_thread != nullptr); +                return cur_thread; +            } +        } + +        return this->template GetObjectWithoutPseudoHandle<T>(handle); +    } + +    ResultCode Reserve(Handle* out_handle); +    void Unreserve(Handle handle); + +    template <typename T> +    ResultCode Add(Handle* out_handle, T* obj) { +        static_assert(std::is_base_of<KAutoObject, T>::value); +        return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken()); +    } + +    template <typename T> +    void Register(Handle handle, T* obj) { +        static_assert(std::is_base_of<KAutoObject, T>::value); +        return this->Register(handle, obj, obj->GetTypeObj().GetClassToken()); +    } + +    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(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<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: +    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<u16>(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 e542b1f07..174318180 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 5c54c6360..62ab26b05 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -11,10 +11,10 @@  #include <unordered_map>  #include <vector>  #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 ebecf0c77..1bfbbcfe2 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 3bc259693..c8acaa453 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 3de0157ac..ef6dfeeca 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 f64e07081..825fab694 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::GlobalSchedulerContext>(kernel); +        global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);          service_thread_manager =              std::make_unique<Common::ThreadWorker>(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<KHandleTable> global_handle_table;      KAutoObjectWithListContainer object_list_container; @@ -722,7 +722,7 @@ KResourceLimit* KernelCore::GetSystemResourceLimit() {  }  KScopedAutoObject<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const { -    return impl->global_handle_table.GetObject<KThread>(handle); +    return impl->global_handle_table->GetObject<KThread>(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 0dd9deaeb..7c46aa997 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> impl; diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 4ccac0b06..fcb8b1ea5 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<s32>(HandleTable::MAX_COUNT); +    handle_table_size = static_cast<s32>(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 156c565b0..d3293a1fe 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<KWritableEvent>(event_handle); diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h index 4af049551..8a8f347b5 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 59ebfc51f..ae9b4be2f 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 f040b4c08..bdfda6c54 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<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask); diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 3dd4acab0..d450345df 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; | 
