|
@@ -12,13 +12,17 @@
|
|
|
typedef u32 Handle;
|
|
typedef u32 Handle;
|
|
|
typedef s32 Result;
|
|
typedef s32 Result;
|
|
|
|
|
|
|
|
|
|
+const Handle INVALID_HANDLE = 0;
|
|
|
|
|
+
|
|
|
namespace Kernel {
|
|
namespace Kernel {
|
|
|
|
|
|
|
|
-// From kernel.h. Declarations duplicated here to avoid a circular header dependency.
|
|
|
|
|
-class Thread;
|
|
|
|
|
-Thread* GetCurrentThread();
|
|
|
|
|
|
|
+// TODO: Verify code
|
|
|
|
|
+const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
|
|
|
|
|
+ ErrorSummary::OutOfResource, ErrorLevel::Temporary);
|
|
|
|
|
+// TOOD: Verify code
|
|
|
|
|
+const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel);
|
|
|
|
|
|
|
|
-enum KernelHandle {
|
|
|
|
|
|
|
+enum KernelHandle : Handle {
|
|
|
CurrentThread = 0xFFFF8000,
|
|
CurrentThread = 0xFFFF8000,
|
|
|
CurrentProcess = 0xFFFF8001,
|
|
CurrentProcess = 0xFFFF8001,
|
|
|
};
|
|
};
|
|
@@ -61,103 +65,127 @@ public:
|
|
|
LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
|
|
LOG_ERROR(Kernel, "(UNIMPLEMENTED)");
|
|
|
return UnimplementedFunction(ErrorModule::Kernel);
|
|
return UnimplementedFunction(ErrorModule::Kernel);
|
|
|
}
|
|
}
|
|
|
-};
|
|
|
|
|
|
|
|
|
|
-class HandleTable : NonCopyable {
|
|
|
|
|
-public:
|
|
|
|
|
- HandleTable();
|
|
|
|
|
- ~HandleTable() {}
|
|
|
|
|
|
|
+private:
|
|
|
|
|
+ friend void intrusive_ptr_add_ref(Object*);
|
|
|
|
|
+ friend void intrusive_ptr_release(Object*);
|
|
|
|
|
|
|
|
- // Allocates a handle within the range and inserts the object into the map.
|
|
|
|
|
- Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF);
|
|
|
|
|
|
|
+ unsigned int ref_count = 0;
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
- static Object* CreateByIDType(int type);
|
|
|
|
|
|
|
+// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting
|
|
|
|
|
+inline void intrusive_ptr_add_ref(Object* object) {
|
|
|
|
|
+ ++object->ref_count;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- template <class T>
|
|
|
|
|
- void Destroy(Handle handle) {
|
|
|
|
|
- if (Get<T>(handle)) {
|
|
|
|
|
- occupied[handle - HANDLE_OFFSET] = false;
|
|
|
|
|
- delete pool[handle - HANDLE_OFFSET];
|
|
|
|
|
- }
|
|
|
|
|
|
|
+inline void intrusive_ptr_release(Object* object) {
|
|
|
|
|
+ if (--object->ref_count == 0) {
|
|
|
|
|
+ delete object;
|
|
|
}
|
|
}
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- bool IsValid(Handle handle) const;
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 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:
|
|
|
|
|
+ HandleTable();
|
|
|
|
|
|
|
|
- template <class T>
|
|
|
|
|
- T* Get(Handle handle) {
|
|
|
|
|
- if (handle == CurrentThread) {
|
|
|
|
|
- return reinterpret_cast<T*>(GetCurrentThread());
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Allocates a handle for the given object.
|
|
|
|
|
+ * @return The created Handle or one of the following errors:
|
|
|
|
|
+ * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded.
|
|
|
|
|
+ */
|
|
|
|
|
+ ResultVal<Handle> Create(Object* obj);
|
|
|
|
|
|
|
|
- if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
|
|
|
|
|
- if (handle != 0) {
|
|
|
|
|
- LOG_ERROR(Kernel, "Bad object handle %08x", handle);
|
|
|
|
|
- }
|
|
|
|
|
- return nullptr;
|
|
|
|
|
- } else {
|
|
|
|
|
- Object* t = pool[handle - HANDLE_OFFSET];
|
|
|
|
|
- if (t->GetHandleType() != T::HANDLE_TYPE) {
|
|
|
|
|
- LOG_ERROR(Kernel, "Wrong object type for %08x", handle);
|
|
|
|
|
- return nullptr;
|
|
|
|
|
- }
|
|
|
|
|
- return static_cast<T*>(t);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 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);
|
|
|
|
|
|
|
|
- // ONLY use this when you know the handle is valid.
|
|
|
|
|
- template <class T>
|
|
|
|
|
- T *GetFast(Handle handle) {
|
|
|
|
|
- if (handle == CurrentThread) {
|
|
|
|
|
- return reinterpret_cast<T*>(GetCurrentThread());
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 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.
|
|
|
|
|
+ */
|
|
|
|
|
+ ResultCode Close(Handle handle);
|
|
|
|
|
|
|
|
- const Handle realHandle = handle - HANDLE_OFFSET;
|
|
|
|
|
- _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
|
|
|
|
|
- return static_cast<T*>(pool[realHandle]);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /// Checks if a handle is valid and points to an existing object.
|
|
|
|
|
+ bool IsValid(Handle handle) const;
|
|
|
|
|
|
|
|
- template <class T, typename ArgT>
|
|
|
|
|
- void Iterate(bool func(T*, ArgT), ArgT arg) {
|
|
|
|
|
- int type = T::GetStaticIDType();
|
|
|
|
|
- for (int i = 0; i < MAX_COUNT; i++)
|
|
|
|
|
- {
|
|
|
|
|
- if (!occupied[i])
|
|
|
|
|
- continue;
|
|
|
|
|
- T* t = static_cast<T*>(pool[i]);
|
|
|
|
|
- if (t->GetIDType() == type) {
|
|
|
|
|
- if (!func(t, arg))
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Looks up a handle.
|
|
|
|
|
+ * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid.
|
|
|
|
|
+ */
|
|
|
|
|
+ Object* GetGeneric(Handle handle) const;
|
|
|
|
|
|
|
|
- bool GetIDType(Handle handle, HandleType* type) const {
|
|
|
|
|
- if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
|
|
|
|
|
- !occupied[handle - HANDLE_OFFSET]) {
|
|
|
|
|
- LOG_ERROR(Kernel, "Bad object handle %08X", handle);
|
|
|
|
|
- return false;
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Looks up a handle while verifying its type.
|
|
|
|
|
+ * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
|
|
|
|
|
+ * type differs from the handle type `T::HANDLE_TYPE`.
|
|
|
|
|
+ */
|
|
|
|
|
+ template <class T>
|
|
|
|
|
+ T* Get(Handle handle) const {
|
|
|
|
|
+ Object* object = GetGeneric(handle);
|
|
|
|
|
+ if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
|
|
|
|
|
+ return static_cast<T*>(object);
|
|
|
}
|
|
}
|
|
|
- Object* t = pool[handle - HANDLE_OFFSET];
|
|
|
|
|
- *type = t->GetHandleType();
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+ return nullptr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- Object* &operator [](Handle handle);
|
|
|
|
|
- void List();
|
|
|
|
|
|
|
+ /// Closes all handles held in this table.
|
|
|
void Clear();
|
|
void Clear();
|
|
|
- int GetCount() const;
|
|
|
|
|
|
|
|
|
|
private:
|
|
private:
|
|
|
|
|
+ /**
|
|
|
|
|
+ * This is the maximum limit of handles allowed per process in CTR-OS. It can be further
|
|
|
|
|
+ * reduced by ExHeader values, but this is not emulated here.
|
|
|
|
|
+ */
|
|
|
|
|
+ static const size_t MAX_COUNT = 4096;
|
|
|
|
|
+
|
|
|
|
|
+ static size_t GetSlot(Handle handle) { return handle >> 15; }
|
|
|
|
|
+ static u16 GetGeneration(Handle handle) { return handle & 0x7FFF; }
|
|
|
|
|
+
|
|
|
|
|
+ /// Stores the Object referenced by the handle or null if the slot is empty.
|
|
|
|
|
+ std::array<Object*, MAX_COUNT> objects;
|
|
|
|
|
|
|
|
- enum {
|
|
|
|
|
- MAX_COUNT = 0x1000,
|
|
|
|
|
- HANDLE_OFFSET = 0x100,
|
|
|
|
|
- INITIAL_NEXT_ID = 0x10,
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 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;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 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;
|
|
|
|
|
|
|
|
- std::array<Object*, MAX_COUNT> pool;
|
|
|
|
|
- std::array<bool, MAX_COUNT> occupied;
|
|
|
|
|
- int next_id;
|
|
|
|
|
|
|
+ /// Head of the free slots linked list.
|
|
|
|
|
+ u16 next_free_slot;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
extern HandleTable g_handle_table;
|
|
extern HandleTable g_handle_table;
|