summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorbunnei <bunneidev@gmail.com>2021-04-24 11:40:31 +0200
committerbunnei <bunneidev@gmail.com>2021-05-06 01:40:53 +0200
commit4b03e6e776e6421c2b2c290b0822b9e5a8556a4c (patch)
tree87c2925a7adf4109a77b4f015cd36d803d4221fc /src/core
parenthle: kernel: KClassToken: Ensure class tokens are correct. (diff)
downloadyuzu-4b03e6e776e6421c2b2c290b0822b9e5a8556a4c.tar
yuzu-4b03e6e776e6421c2b2c290b0822b9e5a8556a4c.tar.gz
yuzu-4b03e6e776e6421c2b2c290b0822b9e5a8556a4c.tar.bz2
yuzu-4b03e6e776e6421c2b2c290b0822b9e5a8556a4c.tar.lz
yuzu-4b03e6e776e6421c2b2c290b0822b9e5a8556a4c.tar.xz
yuzu-4b03e6e776e6421c2b2c290b0822b9e5a8556a4c.tar.zst
yuzu-4b03e6e776e6421c2b2c290b0822b9e5a8556a4c.zip
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/hle/kernel/handle_table.cpp125
-rw-r--r--src/core/hle/kernel/handle_table.h212
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp6
-rw-r--r--src/core/hle/kernel/hle_ipc.h7
-rw-r--r--src/core/hle/kernel/k_auto_object.h2
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp2
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp135
-rw-r--r--src/core/hle/kernel/k_handle_table.h309
-rw-r--r--src/core/hle/kernel/k_process.cpp2
-rw-r--r--src/core/hle/kernel/k_process.h8
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h4
-rw-r--r--src/core/hle/kernel/k_server_session.cpp2
-rw-r--r--src/core/hle/kernel/k_thread.cpp2
-rw-r--r--src/core/hle/kernel/kernel.cpp20
-rw-r--r--src/core/hle/kernel/kernel.h7
-rw-r--r--src/core/hle/kernel/process_capability.cpp4
-rw-r--r--src/core/hle/kernel/svc.cpp8
-rw-r--r--src/core/hle/kernel/svc_common.h15
-rw-r--r--src/core/hle/kernel/time_manager.cpp1
20 files changed, 498 insertions, 377 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"