kernel.h 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // Copyright 2014 Citra Emulator Project / PPSSPP Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <boost/intrusive_ptr.hpp>
  6. #include <array>
  7. #include <string>
  8. #include <vector>
  9. #include "common/common.h"
  10. #include "core/hle/result.h"
  11. typedef u32 Handle;
  12. typedef s32 Result;
  13. const Handle INVALID_HANDLE = 0;
  14. namespace Kernel {
  15. class Thread;
  16. // TODO: Verify code
  17. const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
  18. ErrorSummary::OutOfResource, ErrorLevel::Temporary);
  19. // TOOD: Verify code
  20. const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel);
  21. enum KernelHandle : Handle {
  22. CurrentThread = 0xFFFF8000,
  23. CurrentProcess = 0xFFFF8001,
  24. };
  25. enum class HandleType : u32 {
  26. Unknown = 0,
  27. Port = 1,
  28. Session = 2,
  29. Event = 3,
  30. Mutex = 4,
  31. SharedMemory = 5,
  32. Redirection = 6,
  33. Thread = 7,
  34. Process = 8,
  35. AddressArbiter = 9,
  36. Semaphore = 10,
  37. Timer = 11
  38. };
  39. enum {
  40. DEFAULT_STACK_SIZE = 0x4000,
  41. };
  42. class HandleTable;
  43. class Object : NonCopyable {
  44. friend class HandleTable;
  45. u32 handle = INVALID_HANDLE;
  46. public:
  47. virtual ~Object() {}
  48. Handle GetHandle() const { return handle; }
  49. virtual std::string GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; }
  50. virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; }
  51. virtual Kernel::HandleType GetHandleType() const = 0;
  52. /**
  53. * Check if a thread can wait on the object
  54. * @return True if a thread can wait on the object, otherwise false
  55. */
  56. bool IsWaitable() const {
  57. switch (GetHandleType()) {
  58. case HandleType::Session:
  59. case HandleType::Event:
  60. case HandleType::Mutex:
  61. case HandleType::Thread:
  62. case HandleType::Semaphore:
  63. case HandleType::Timer:
  64. return true;
  65. case HandleType::Unknown:
  66. case HandleType::Port:
  67. case HandleType::SharedMemory:
  68. case HandleType::Redirection:
  69. case HandleType::Process:
  70. case HandleType::AddressArbiter:
  71. return false;
  72. }
  73. return false;
  74. }
  75. private:
  76. friend void intrusive_ptr_add_ref(Object*);
  77. friend void intrusive_ptr_release(Object*);
  78. unsigned int ref_count = 0;
  79. };
  80. // Special functions used by boost::instrusive_ptr to do automatic ref-counting
  81. inline void intrusive_ptr_add_ref(Object* object) {
  82. ++object->ref_count;
  83. }
  84. inline void intrusive_ptr_release(Object* object) {
  85. if (--object->ref_count == 0) {
  86. delete object;
  87. }
  88. }
  89. template <typename T>
  90. using SharedPtr = boost::intrusive_ptr<T>;
  91. /// Class that represents a Kernel object that a thread can be waiting on
  92. class WaitObject : public Object {
  93. public:
  94. /**
  95. * Check if the current thread should wait until the object is available
  96. * @return True if the current thread should wait due to this object being unavailable
  97. */
  98. virtual bool ShouldWait() = 0;
  99. /// Acquire/lock the object if it is available
  100. virtual void Acquire() = 0;
  101. /**
  102. * Add a thread to wait on this object
  103. * @param thread Pointer to thread to add
  104. */
  105. void AddWaitingThread(Thread* thread);
  106. /**
  107. * Removes a thread from waiting on this object (e.g. if it was resumed already)
  108. * @param thread Pointer to thread to remove
  109. */
  110. void RemoveWaitingThread(Thread* thead);
  111. /**
  112. * Wake up the next thread waiting on this object
  113. * @return Pointer to the thread that was resumed, nullptr if no threads are waiting
  114. */
  115. Thread* WakeupNextThread();
  116. /// Wake up all threads waiting on this object
  117. void WakeupAllWaitingThreads();
  118. private:
  119. std::vector<Thread*> waiting_threads; ///< Threads waiting for this object to become available
  120. };
  121. /**
  122. * This class allows the creation of Handles, which are references to objects that can be tested
  123. * for validity and looked up. Here they are used to pass references to kernel objects to/from the
  124. * emulated process. it has been designed so that it follows the same handle format and has
  125. * approximately the same restrictions as the handle manager in the CTR-OS.
  126. *
  127. * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0).
  128. * The slot index is used to index into the arrays in this class to access the data corresponding
  129. * to the Handle.
  130. *
  131. * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter
  132. * is kept and incremented every time a Handle is created. This is the Handle's "generation". The
  133. * value of the counter is stored into the Handle as well as in the handle table (in the
  134. * "generations" array). When looking up a handle, the Handle's generation must match with the
  135. * value stored on the class, otherwise the Handle is considered invalid.
  136. *
  137. * To find free slots when allocating a Handle without needing to scan the entire object array, the
  138. * generations field of unallocated slots is re-purposed as a linked list of indices to free slots.
  139. * When a Handle is created, an index is popped off the list and used for the new Handle. When it
  140. * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is
  141. * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been
  142. * verified and isn't likely to cause any problems.
  143. */
  144. class HandleTable final : NonCopyable {
  145. public:
  146. HandleTable();
  147. /**
  148. * Allocates a handle for the given object.
  149. * @return The created Handle or one of the following errors:
  150. * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
  151. */
  152. ResultVal<Handle> Create(SharedPtr<Object> obj);
  153. /**
  154. * Returns a new handle that points to the same object as the passed in handle.
  155. * @return The duplicated Handle or one of the following errors:
  156. * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
  157. * - Any errors returned by `Create()`.
  158. */
  159. ResultVal<Handle> Duplicate(Handle handle);
  160. /**
  161. * Closes a handle, removing it from the table and decreasing the object's ref-count.
  162. * @return `RESULT_SUCCESS` or one of the following errors:
  163. * - `ERR_INVALID_HANDLE`: an invalid handle was passed in.
  164. */
  165. ResultCode Close(Handle handle);
  166. /// Checks if a handle is valid and points to an existing object.
  167. bool IsValid(Handle handle) const;
  168. /**
  169. * Looks up a handle.
  170. * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
  171. */
  172. SharedPtr<Object> GetGeneric(Handle handle) const;
  173. /**
  174. * Looks up a handle while verifying its type.
  175. * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
  176. * type differs from the handle type `T::HANDLE_TYPE`.
  177. */
  178. template <class T>
  179. SharedPtr<T> Get(Handle handle) const {
  180. SharedPtr<Object> object = GetGeneric(handle);
  181. if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
  182. return boost::static_pointer_cast<T>(std::move(object));
  183. }
  184. return nullptr;
  185. }
  186. /**
  187. * Looks up a handle while verifying that it is an object that a thread can wait on
  188. * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is
  189. * not a waitable object.
  190. */
  191. SharedPtr<WaitObject> GetWaitObject(Handle handle) const {
  192. SharedPtr<Object> object = GetGeneric(handle);
  193. if (object != nullptr && object->IsWaitable()) {
  194. return boost::static_pointer_cast<WaitObject>(std::move(object));
  195. }
  196. return nullptr;
  197. }
  198. /// Closes all handles held in this table.
  199. void Clear();
  200. private:
  201. /**
  202. * This is the maximum limit of handles allowed per process in CTR-OS. It can be further
  203. * reduced by ExHeader values, but this is not emulated here.
  204. */
  205. static const size_t MAX_COUNT = 4096;
  206. static size_t GetSlot(Handle handle) { return handle >> 15; }
  207. static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; }
  208. /// Stores the Object referenced by the handle or null if the slot is empty.
  209. std::array<SharedPtr<Object>, MAX_COUNT> objects;
  210. /**
  211. * The value of `next_generation` when the handle was created, used to check for validity. For
  212. * empty slots, contains the index of the next free slot in the list.
  213. */
  214. std::array<u16, MAX_COUNT> generations;
  215. /**
  216. * Global counter of the number of created handles. Stored in `generations` when a handle is
  217. * created, and wraps around to 1 when it hits 0x8000.
  218. */
  219. u16 next_generation;
  220. /// Head of the free slots linked list.
  221. u16 next_free_slot;
  222. };
  223. extern HandleTable g_handle_table;
  224. extern SharedPtr<Thread> g_main_thread;
  225. /// The ID code of the currently running game
  226. /// TODO(Subv): This variable should not be here,
  227. /// we need a way to store information about the currently loaded application
  228. /// for later query during runtime, maybe using the LDR service?
  229. extern u64 g_program_id;
  230. /// Initialize the kernel
  231. void Init();
  232. /// Shutdown the kernel
  233. void Shutdown();
  234. /**
  235. * Loads executable stored at specified address
  236. * @entry_point Entry point in memory of loaded executable
  237. * @return True on success, otherwise false
  238. */
  239. bool LoadExec(u32 entry_point);
  240. } // namespace