22 changed files with 503 additions and 381 deletions
-
4src/core/CMakeLists.txt
-
125src/core/hle/kernel/handle_table.cpp
-
212src/core/hle/kernel/handle_table.h
-
6src/core/hle/kernel/hle_ipc.cpp
-
7src/core/hle/kernel/hle_ipc.h
-
2src/core/hle/kernel/k_auto_object.h
-
2src/core/hle/kernel/k_condition_variable.cpp
-
135src/core/hle/kernel/k_handle_table.cpp
-
309src/core/hle/kernel/k_handle_table.h
-
2src/core/hle/kernel/k_process.cpp
-
8src/core/hle/kernel/k_process.h
-
4src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
-
2src/core/hle/kernel/k_server_session.cpp
-
2src/core/hle/kernel/k_thread.cpp
-
20src/core/hle/kernel/kernel.cpp
-
7src/core/hle/kernel/kernel.h
-
4src/core/hle/kernel/process_capability.cpp
-
8src/core/hle/kernel/svc.cpp
-
15src/core/hle/kernel/svc_common.h
-
1src/core/hle/kernel/time_manager.cpp
-
4src/yuzu/debugger/wait_tree.cpp
-
5src/yuzu/debugger/wait_tree.h
@ -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
|
|||
@ -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 |
|||
@ -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
|
|||
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue