k_handle_table.h 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <array>
  5. #include "common/assert.h"
  6. #include "common/bit_field.h"
  7. #include "common/common_types.h"
  8. #include "core/hle/kernel/k_auto_object.h"
  9. #include "core/hle/kernel/k_spin_lock.h"
  10. #include "core/hle/kernel/k_thread.h"
  11. #include "core/hle/kernel/kernel.h"
  12. #include "core/hle/kernel/svc_common.h"
  13. #include "core/hle/kernel/svc_results.h"
  14. #include "core/hle/result.h"
  15. namespace Kernel {
  16. class KernelCore;
  17. class KHandleTable {
  18. YUZU_NON_COPYABLE(KHandleTable);
  19. YUZU_NON_MOVEABLE(KHandleTable);
  20. public:
  21. static constexpr size_t MaxTableSize = 1024;
  22. public:
  23. explicit KHandleTable(KernelCore& kernel) : m_kernel(kernel) {}
  24. Result Initialize(s32 size) {
  25. // Check that the table size is valid.
  26. R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
  27. // Lock.
  28. KScopedDisableDispatch dd{m_kernel};
  29. KScopedSpinLock lk(m_lock);
  30. // Initialize all fields.
  31. m_max_count = 0;
  32. m_table_size = static_cast<s16>((size <= 0) ? MaxTableSize : size);
  33. m_next_linear_id = MinLinearId;
  34. m_count = 0;
  35. m_free_head_index = -1;
  36. // Free all entries.
  37. for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
  38. m_objects[i] = nullptr;
  39. m_entry_infos[i].next_free_index = static_cast<s16>(i - 1);
  40. m_free_head_index = i;
  41. }
  42. R_SUCCEED();
  43. }
  44. size_t GetTableSize() const {
  45. return m_table_size;
  46. }
  47. size_t GetCount() const {
  48. return m_count;
  49. }
  50. size_t GetMaxCount() const {
  51. return m_max_count;
  52. }
  53. Result Finalize();
  54. bool Remove(Handle handle);
  55. template <typename T = KAutoObject>
  56. KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
  57. // Lock and look up in table.
  58. KScopedDisableDispatch dd{m_kernel};
  59. KScopedSpinLock lk(m_lock);
  60. if constexpr (std::is_same_v<T, KAutoObject>) {
  61. return this->GetObjectImpl(handle);
  62. } else {
  63. if (auto* obj = this->GetObjectImpl(handle); obj != nullptr) [[likely]] {
  64. return obj->DynamicCast<T*>();
  65. } else {
  66. return nullptr;
  67. }
  68. }
  69. }
  70. template <typename T = KAutoObject>
  71. KScopedAutoObject<T> GetObject(Handle handle) const {
  72. // Handle pseudo-handles.
  73. if constexpr (std::derived_from<KProcess, T>) {
  74. if (handle == Svc::PseudoHandle::CurrentProcess) {
  75. auto* const cur_process = GetCurrentProcessPointer(m_kernel);
  76. ASSERT(cur_process != nullptr);
  77. return cur_process;
  78. }
  79. } else if constexpr (std::derived_from<KThread, T>) {
  80. if (handle == Svc::PseudoHandle::CurrentThread) {
  81. auto* const cur_thread = GetCurrentThreadPointer(m_kernel);
  82. ASSERT(cur_thread != nullptr);
  83. return cur_thread;
  84. }
  85. }
  86. return this->template GetObjectWithoutPseudoHandle<T>(handle);
  87. }
  88. KScopedAutoObject<KAutoObject> GetObjectForIpcWithoutPseudoHandle(Handle handle) const {
  89. // Lock and look up in table.
  90. KScopedDisableDispatch dd{m_kernel};
  91. KScopedSpinLock lk(m_lock);
  92. return this->GetObjectImpl(handle);
  93. }
  94. KScopedAutoObject<KAutoObject> GetObjectForIpc(Handle handle, KThread* cur_thread) const;
  95. KScopedAutoObject<KAutoObject> GetObjectByIndex(Handle* out_handle, size_t index) const {
  96. KScopedDisableDispatch dd{m_kernel};
  97. KScopedSpinLock lk(m_lock);
  98. return this->GetObjectByIndexImpl(out_handle, index);
  99. }
  100. Result Reserve(Handle* out_handle);
  101. void Unreserve(Handle handle);
  102. Result Add(Handle* out_handle, KAutoObject* obj);
  103. void Register(Handle handle, KAutoObject* obj);
  104. template <typename T>
  105. bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
  106. // Try to convert and open all the handles.
  107. size_t num_opened;
  108. {
  109. // Lock the table.
  110. KScopedDisableDispatch dd{m_kernel};
  111. KScopedSpinLock lk(m_lock);
  112. for (num_opened = 0; num_opened < num_handles; num_opened++) {
  113. // Get the current handle.
  114. const auto cur_handle = handles[num_opened];
  115. // Get the object for the current handle.
  116. KAutoObject* cur_object = this->GetObjectImpl(cur_handle);
  117. if (cur_object == nullptr) [[unlikely]] {
  118. break;
  119. }
  120. // Cast the current object to the desired type.
  121. T* cur_t = cur_object->DynamicCast<T*>();
  122. if (cur_t == nullptr) [[unlikely]] {
  123. break;
  124. }
  125. // Open a reference to the current object.
  126. cur_t->Open();
  127. out[num_opened] = cur_t;
  128. }
  129. }
  130. // If we converted every object, succeed.
  131. if (num_opened == num_handles) [[likely]] {
  132. return true;
  133. }
  134. // If we didn't convert entry object, close the ones we opened.
  135. for (size_t i = 0; i < num_opened; i++) {
  136. out[i]->Close();
  137. }
  138. return false;
  139. }
  140. private:
  141. s32 AllocateEntry() {
  142. ASSERT(m_count < m_table_size);
  143. const auto index = m_free_head_index;
  144. m_free_head_index = m_entry_infos[index].GetNextFreeIndex();
  145. m_max_count = std::max(m_max_count, ++m_count);
  146. return index;
  147. }
  148. void FreeEntry(s32 index) {
  149. ASSERT(m_count > 0);
  150. m_objects[index] = nullptr;
  151. m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
  152. m_free_head_index = index;
  153. --m_count;
  154. }
  155. u16 AllocateLinearId() {
  156. const u16 id = m_next_linear_id++;
  157. if (m_next_linear_id > MaxLinearId) {
  158. m_next_linear_id = MinLinearId;
  159. }
  160. return id;
  161. }
  162. bool IsValidHandle(Handle handle) const {
  163. // Unpack the handle.
  164. const auto handle_pack = HandlePack(handle);
  165. const auto raw_value = handle_pack.raw;
  166. const auto index = handle_pack.index;
  167. const auto linear_id = handle_pack.linear_id;
  168. const auto reserved = handle_pack.reserved;
  169. ASSERT(reserved == 0);
  170. // Validate our indexing information.
  171. if (raw_value == 0) [[unlikely]] {
  172. return false;
  173. }
  174. if (linear_id == 0) [[unlikely]] {
  175. return false;
  176. }
  177. if (index >= m_table_size) [[unlikely]] {
  178. return false;
  179. }
  180. // Check that there's an object, and our serial id is correct.
  181. if (m_objects[index] == nullptr) [[unlikely]] {
  182. return false;
  183. }
  184. if (m_entry_infos[index].GetLinearId() != linear_id) [[unlikely]] {
  185. return false;
  186. }
  187. return true;
  188. }
  189. KAutoObject* GetObjectImpl(Handle handle) const {
  190. // Handles must not have reserved bits set.
  191. const auto handle_pack = HandlePack(handle);
  192. if (handle_pack.reserved != 0) [[unlikely]] {
  193. return nullptr;
  194. }
  195. if (this->IsValidHandle(handle)) [[likely]] {
  196. return m_objects[handle_pack.index];
  197. } else {
  198. return nullptr;
  199. }
  200. }
  201. KAutoObject* GetObjectByIndexImpl(Handle* out_handle, size_t index) const {
  202. // Index must be in bounds.
  203. if (index >= m_table_size) [[unlikely]] {
  204. return nullptr;
  205. }
  206. // Ensure entry has an object.
  207. if (KAutoObject* obj = m_objects[index]; obj != nullptr) {
  208. *out_handle = EncodeHandle(static_cast<u16>(index), m_entry_infos[index].GetLinearId());
  209. return obj;
  210. } else {
  211. return nullptr;
  212. }
  213. }
  214. private:
  215. union HandlePack {
  216. constexpr HandlePack() = default;
  217. constexpr HandlePack(Handle handle) : raw{static_cast<u32>(handle)} {}
  218. u32 raw{};
  219. BitField<0, 15, u32> index;
  220. BitField<15, 15, u32> linear_id;
  221. BitField<30, 2, u32> reserved;
  222. };
  223. static constexpr Handle EncodeHandle(u16 index, u16 linear_id) {
  224. HandlePack handle{};
  225. handle.index.Assign(index);
  226. handle.linear_id.Assign(linear_id);
  227. handle.reserved.Assign(0);
  228. return handle.raw;
  229. }
  230. private:
  231. static constexpr u16 MinLinearId = 1;
  232. static constexpr u16 MaxLinearId = 0x7FFF;
  233. union EntryInfo {
  234. u16 linear_id;
  235. s16 next_free_index;
  236. constexpr u16 GetLinearId() const {
  237. return linear_id;
  238. }
  239. constexpr s32 GetNextFreeIndex() const {
  240. return next_free_index;
  241. }
  242. };
  243. private:
  244. KernelCore& m_kernel;
  245. std::array<EntryInfo, MaxTableSize> m_entry_infos{};
  246. std::array<KAutoObject*, MaxTableSize> m_objects{};
  247. mutable KSpinLock m_lock;
  248. s32 m_free_head_index{};
  249. u16 m_table_size{};
  250. u16 m_max_count{};
  251. u16 m_next_linear_id{};
  252. u16 m_count{};
  253. };
  254. } // namespace Kernel