aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp7
-rw-r--r--src/core/hle/kernel/address_arbiter.h1
-rw-r--r--src/core/hle/kernel/client_port.cpp4
-rw-r--r--src/core/hle/kernel/client_port.h1
-rw-r--r--src/core/hle/kernel/client_session.cpp3
-rw-r--r--src/core/hle/kernel/client_session.h3
-rw-r--r--src/core/hle/kernel/errors.h98
-rw-r--r--src/core/hle/kernel/event.h1
-rw-r--r--src/core/hle/kernel/handle_table.cpp97
-rw-r--r--src/core/hle/kernel/handle_table.h126
-rw-r--r--src/core/hle/kernel/kernel.cpp163
-rw-r--r--src/core/hle/kernel/kernel.h188
-rw-r--r--src/core/hle/kernel/memory.cpp1
-rw-r--r--src/core/hle/kernel/mutex.h1
-rw-r--r--src/core/hle/kernel/process.cpp1
-rw-r--r--src/core/hle/kernel/resource_limit.cpp1
-rw-r--r--src/core/hle/kernel/semaphore.cpp7
-rw-r--r--src/core/hle/kernel/semaphore.h2
-rw-r--r--src/core/hle/kernel/server_port.h1
-rw-r--r--src/core/hle/kernel/server_session.h3
-rw-r--r--src/core/hle/kernel/shared_memory.cpp16
-rw-r--r--src/core/hle/kernel/thread.cpp25
-rw-r--r--src/core/hle/kernel/thread.h3
-rw-r--r--src/core/hle/kernel/timer.cpp1
-rw-r--r--src/core/hle/kernel/timer.h1
-rw-r--r--src/core/hle/kernel/vm_manager.cpp1
-rw-r--r--src/core/hle/kernel/vm_manager.h8
-rw-r--r--src/core/hle/kernel/wait_object.cpp99
-rw-r--r--src/core/hle/kernel/wait_object.h67
29 files changed, 547 insertions, 383 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 01fab123e..776d342f0 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -5,6 +5,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/address_arbiter.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h"
@@ -74,8 +75,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
default:
LOG_ERROR(Kernel, "unknown type=%d", type);
- return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel,
- ErrorSummary::WrongArgument, ErrorLevel::Usage);
+ return ERR_INVALID_ENUM_VALUE_FND;
}
// The calls that use a timeout seem to always return a Timeout error even if they did not put
@@ -83,8 +83,7 @@ ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address,
if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
- return ResultCode(ErrorDescription::Timeout, ErrorModule::OS, ErrorSummary::StatusChanged,
- ErrorLevel::Info);
+ return RESULT_TIMEOUT;
}
return RESULT_SUCCESS;
}
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index 6a7af93a9..1d24401b1 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -6,6 +6,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
// Address arbiters are an underlying kernel synchronization object that can be created/used via
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index ddcf4c916..03ffdece1 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h"
@@ -19,8 +20,7 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// AcceptSession before returning from this call.
if (active_sessions >= max_sessions) {
- return ResultCode(ErrorDescription::MaxConnectionsReached, ErrorModule::OS,
- ErrorSummary::WouldBlock, ErrorLevel::Temporary);
+ return ERR_MAX_CONNECTIONS_REACHED;
}
active_sessions++;
diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h
index 511490c7c..8f7d6ac44 100644
--- a/src/core/hle/kernel/client_port.h
+++ b/src/core/hle/kernel/client_port.h
@@ -7,6 +7,7 @@
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index e297b7464..783b1c061 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -30,8 +30,7 @@ ResultCode ClientSession::SendSyncRequest() {
if (parent->server)
return parent->server->HandleSyncRequest();
- return ResultCode(ErrorDescription::SessionClosedByRemote, ErrorModule::OS,
- ErrorSummary::Canceled, ErrorLevel::Status);
+ return ERR_SESSION_CLOSED_BY_REMOTE;
}
} // namespace
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 9f3adb72b..2de379c09 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -6,10 +6,9 @@
#include <memory>
#include <string>
-
#include "common/common_types.h"
-
#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
new file mode 100644
index 000000000..b3b60e7df
--- /dev/null
+++ b/src/core/hle/kernel/errors.h
@@ -0,0 +1,98 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+namespace ErrCodes {
+enum {
+ OutOfHandles = 19,
+ SessionClosedByRemote = 26,
+ PortNameTooLong = 30,
+ WrongPermission = 46,
+ InvalidBufferDescriptor = 48,
+ MaxConnectionsReached = 52,
+};
+}
+
+// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
+// double check that the code matches before re-using the constant.
+
+constexpr ResultCode ERR_OUT_OF_HANDLES(ErrCodes::OutOfHandles, ErrorModule::Kernel,
+ ErrorSummary::OutOfResource,
+ ErrorLevel::Permanent); // 0xD8600413
+constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrCodes::SessionClosedByRemote, ErrorModule::OS,
+ ErrorSummary::Canceled,
+ ErrorLevel::Status); // 0xC920181A
+constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrCodes::PortNameTooLong, ErrorModule::OS,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Usage); // 0xE0E0181E
+constexpr ResultCode ERR_WRONG_PERMISSION(ErrCodes::WrongPermission, ErrorModule::OS,
+ ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+constexpr ResultCode ERR_INVALID_BUFFER_DESCRIPTOR(ErrCodes::InvalidBufferDescriptor,
+ ErrorModule::OS, ErrorSummary::WrongArgument,
+ ErrorLevel::Permanent);
+constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrCodes::MaxConnectionsReached, ErrorModule::OS,
+ ErrorSummary::WouldBlock,
+ ErrorLevel::Temporary); // 0xD0401834
+
+constexpr ResultCode ERR_NOT_AUTHORIZED(ErrorDescription::NotAuthorized, ErrorModule::OS,
+ ErrorSummary::WrongArgument,
+ ErrorLevel::Permanent); // 0xD9001BEA
+constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Permanent); // 0xD8E007ED
+constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(ErrorDescription::InvalidEnumValue,
+ ErrorModule::FND, ErrorSummary::InvalidArgument,
+ ErrorLevel::Permanent); // 0xD8E093ED
+constexpr ResultCode ERR_INVALID_COMBINATION(ErrorDescription::InvalidCombination, ErrorModule::OS,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Usage); // 0xE0E01BEE
+constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorDescription::InvalidCombination,
+ ErrorModule::Kernel,
+ ErrorSummary::WrongArgument,
+ ErrorLevel::Permanent); // 0xD90007EE
+constexpr ResultCode ERR_MISALIGNED_ADDRESS(ErrorDescription::MisalignedAddress, ErrorModule::OS,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Usage); // 0xE0E01BF1
+constexpr ResultCode ERR_MISALIGNED_SIZE(ErrorDescription::MisalignedSize, ErrorModule::OS,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Usage); // 0xE0E01BF2
+constexpr ResultCode ERR_OUT_OF_MEMORY(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
+ ErrorSummary::OutOfResource,
+ ErrorLevel::Permanent); // 0xD86007F3
+constexpr ResultCode ERR_NOT_IMPLEMENTED(ErrorDescription::NotImplemented, ErrorModule::OS,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Usage); // 0xE0E01BF4
+constexpr ResultCode ERR_INVALID_ADDRESS(ErrorDescription::InvalidAddress, ErrorModule::OS,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Usage); // 0xE0E01BF5
+constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorDescription::InvalidAddress, ErrorModule::OS,
+ ErrorSummary::InvalidState,
+ ErrorLevel::Usage); // 0xE0A01BF5
+constexpr ResultCode ERR_INVALID_POINTER(ErrorDescription::InvalidPointer, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Permanent); // 0xD8E007F6
+constexpr ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Permanent); // 0xD8E007F7
+/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
+constexpr ResultCode ERR_INVALID_HANDLE_OS(ErrorDescription::InvalidHandle, ErrorModule::OS,
+ ErrorSummary::WrongArgument,
+ ErrorLevel::Permanent); // 0xD9001BF7
+constexpr ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel,
+ ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA
+constexpr ResultCode ERR_OUT_OF_RANGE(ErrorDescription::OutOfRange, ErrorModule::OS,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Usage); // 0xE0E01BFD
+constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(ErrorDescription::OutOfRange, ErrorModule::Kernel,
+ ErrorSummary::InvalidArgument,
+ ErrorLevel::Permanent); // 0xD8E007FD
+constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS,
+ ErrorSummary::StatusChanged, ErrorLevel::Info);
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index 3e3673508..cc41abb85 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -6,6 +6,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/wait_object.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
new file mode 100644
index 000000000..c7322d883
--- /dev/null
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -0,0 +1,97 @@
+// 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/hle/kernel/errors.h"
+#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+HandleTable g_handle_table;
+
+HandleTable::HandleTable() {
+ next_generation = 1;
+ Clear();
+}
+
+ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
+ DEBUG_ASSERT(obj != nullptr);
+
+ u16 slot = next_free_slot;
+ if (slot >= generations.size()) {
+ LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
+ return ERR_OUT_OF_HANDLES;
+ }
+ next_free_slot = generations[slot];
+
+ u16 generation = next_generation++;
+
+ // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
+ // CTR-OS doesn't use generation 0, so skip straight to 1.
+ if (next_generation >= (1 << 15))
+ next_generation = 1;
+
+ generations[slot] = generation;
+ objects[slot] = std::move(obj);
+
+ Handle handle = generation | (slot << 15);
+ return MakeResult<Handle>(handle);
+}
+
+ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
+ SharedPtr<Object> object = GetGeneric(handle);
+ if (object == nullptr) {
+ LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
+ return ERR_INVALID_HANDLE;
+ }
+ return Create(std::move(object));
+}
+
+ResultCode HandleTable::Close(Handle handle) {
+ if (!IsValid(handle))
+ return ERR_INVALID_HANDLE;
+
+ u16 slot = GetSlot(handle);
+
+ objects[slot] = nullptr;
+
+ generations[slot] = next_free_slot;
+ next_free_slot = slot;
+ return RESULT_SUCCESS;
+}
+
+bool HandleTable::IsValid(Handle handle) const {
+ size_t slot = GetSlot(handle);
+ u16 generation = GetGeneration(handle);
+
+ return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
+}
+
+SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
+ if (handle == CurrentThread) {
+ return GetCurrentThread();
+ } else if (handle == CurrentProcess) {
+ return g_current_process;
+ }
+
+ if (!IsValid(handle)) {
+ return nullptr;
+ }
+ return objects[GetSlot(handle)];
+}
+
+void HandleTable::Clear() {
+ for (u16 i = 0; i < MAX_COUNT; ++i) {
+ generations[i] = i + 1;
+ objects[i] = nullptr;
+ }
+ next_free_slot = 0;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
new file mode 100644
index 000000000..d6aaefbf7
--- /dev/null
+++ b/src/core/hle/kernel/handle_table.h
@@ -0,0 +1,126 @@
+// 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 "common/common_types.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+enum KernelHandle : Handle {
+ 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:
+ HandleTable();
+
+ /**
+ * 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(SharedPtr<Object> obj);
+
+ /**
+ * 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.
+ */
+ ResultCode Close(Handle handle);
+
+ /// Checks if a handle is valid and points to an existing object.
+ bool IsValid(Handle handle) const;
+
+ /**
+ * Looks up a handle.
+ * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
+ */
+ SharedPtr<Object> GetGeneric(Handle handle) const;
+
+ /**
+ * Looks up a handle while verifying its type.
+ * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its
+ * type differs from the requested one.
+ */
+ template <class T>
+ SharedPtr<T> Get(Handle handle) const {
+ return DynamicObjectCast<T>(GetGeneric(handle));
+ }
+
+ /// Closes all handles held in this table.
+ void Clear();
+
+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 u16 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<SharedPtr<Object>, 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;
+
+ /**
+ * 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;
+
+ /// Head of the free slots linked list.
+ u16 next_free_slot;
+};
+
+extern HandleTable g_handle_table;
+
+} // namespace
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index f599916f0..7470a97ca 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -2,10 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
-#include "common/assert.h"
-#include "common/logging/log.h"
#include "core/hle/config_mem.h"
+#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
@@ -17,165 +15,6 @@
namespace Kernel {
unsigned int Object::next_object_id;
-HandleTable g_handle_table;
-
-void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
- auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
- if (itr == waiting_threads.end())
- waiting_threads.push_back(std::move(thread));
-}
-
-void WaitObject::RemoveWaitingThread(Thread* thread) {
- auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
- // If a thread passed multiple handles to the same object,
- // the kernel might attempt to remove the thread from the object's
- // waiting threads list multiple times.
- if (itr != waiting_threads.end())
- waiting_threads.erase(itr);
-}
-
-SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
- Thread* candidate = nullptr;
- s32 candidate_priority = THREADPRIO_LOWEST + 1;
-
- for (const auto& thread : waiting_threads) {
- // The list of waiting threads must not contain threads that are not waiting to be awakened.
- ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
- thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
- "Inconsistent thread statuses in waiting_threads");
-
- if (thread->current_priority >= candidate_priority)
- continue;
-
- if (ShouldWait(thread.get()))
- continue;
-
- // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or
- // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready.
- bool ready_to_run = true;
- if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) {
- ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
- [&thread](const SharedPtr<WaitObject>& object) {
- return object->ShouldWait(thread.get());
- });
- }
-
- if (ready_to_run) {
- candidate = thread.get();
- candidate_priority = thread->current_priority;
- }
- }
-
- return candidate;
-}
-
-void WaitObject::WakeupAllWaitingThreads() {
- while (auto thread = GetHighestPriorityReadyThread()) {
- if (!thread->IsSleepingOnWaitAll()) {
- Acquire(thread.get());
- // Set the output index of the WaitSynchronizationN call to the index of this object.
- if (thread->wait_set_output) {
- thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this));
- thread->wait_set_output = false;
- }
- } else {
- for (auto& object : thread->wait_objects) {
- object->Acquire(thread.get());
- }
- // Note: This case doesn't update the output index of WaitSynchronizationN.
- }
-
- for (auto& object : thread->wait_objects)
- object->RemoveWaitingThread(thread.get());
- thread->wait_objects.clear();
-
- thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
- thread->ResumeFromWait();
- }
-}
-
-const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const {
- return waiting_threads;
-}
-
-HandleTable::HandleTable() {
- next_generation = 1;
- Clear();
-}
-
-ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
- DEBUG_ASSERT(obj != nullptr);
-
- u16 slot = next_free_slot;
- if (slot >= generations.size()) {
- LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
- return ERR_OUT_OF_HANDLES;
- }
- next_free_slot = generations[slot];
-
- u16 generation = next_generation++;
-
- // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
- // CTR-OS doesn't use generation 0, so skip straight to 1.
- if (next_generation >= (1 << 15))
- next_generation = 1;
-
- generations[slot] = generation;
- objects[slot] = std::move(obj);
-
- Handle handle = generation | (slot << 15);
- return MakeResult<Handle>(handle);
-}
-
-ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
- SharedPtr<Object> object = GetGeneric(handle);
- if (object == nullptr) {
- LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle);
- return ERR_INVALID_HANDLE;
- }
- return Create(std::move(object));
-}
-
-ResultCode HandleTable::Close(Handle handle) {
- if (!IsValid(handle))
- return ERR_INVALID_HANDLE;
-
- u16 slot = GetSlot(handle);
-
- objects[slot] = nullptr;
-
- generations[slot] = next_free_slot;
- next_free_slot = slot;
- return RESULT_SUCCESS;
-}
-
-bool HandleTable::IsValid(Handle handle) const {
- size_t slot = GetSlot(handle);
- u16 generation = GetGeneration(handle);
-
- return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
-}
-
-SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
- if (handle == CurrentThread) {
- return GetCurrentThread();
- } else if (handle == CurrentProcess) {
- return g_current_process;
- }
-
- if (!IsValid(handle)) {
- return nullptr;
- }
- return objects[GetSlot(handle)];
-}
-
-void HandleTable::Clear() {
- for (u16 i = 0; i < MAX_COUNT; ++i) {
- generations[i] = i + 1;
- objects[i] = nullptr;
- }
- next_free_slot = 0;
-}
/// Initialize the kernel
void Init(u32 system_mode) {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index bb8b99bb5..9cf288b08 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -4,33 +4,16 @@
#pragma once
-#include <algorithm>
-#include <array>
#include <cstddef>
#include <string>
-#include <vector>
+#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include "common/common_types.h"
-#include "core/hle/result.h"
namespace Kernel {
using Handle = u32;
-class Thread;
-
-// 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(ErrorDescription::InvalidHandle, ErrorModule::Kernel,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
-
-enum KernelHandle : Handle {
- CurrentThread = 0xFFFF8000,
- CurrentProcess = 0xFFFF8001,
-};
-
enum class HandleType : u32 {
Unknown,
Event,
@@ -128,170 +111,17 @@ inline void intrusive_ptr_release(Object* object) {
template <typename T>
using SharedPtr = boost::intrusive_ptr<T>;
-/// Class that represents a Kernel object that a thread can be waiting on
-class WaitObject : public Object {
-public:
- /**
- * Check if the specified thread should wait until the object is available
- * @param thread The thread about which we're deciding.
- * @return True if the current thread should wait due to this object being unavailable
- */
- virtual bool ShouldWait(Thread* thread) const = 0;
-
- /// Acquire/lock the object for the specified thread if it is available
- virtual void Acquire(Thread* thread) = 0;
-
- /**
- * Add a thread to wait on this object
- * @param thread Pointer to thread to add
- */
- virtual void AddWaitingThread(SharedPtr<Thread> thread);
-
- /**
- * Removes a thread from waiting on this object (e.g. if it was resumed already)
- * @param thread Pointer to thread to remove
- */
- virtual void RemoveWaitingThread(Thread* thread);
-
- /**
- * Wake up all threads waiting on this object that can be awoken, in priority order,
- * and set the synchronization result and output of the thread.
- */
- virtual void WakeupAllWaitingThreads();
-
- /// Obtains the highest priority thread that is ready to run from this object's waiting list.
- SharedPtr<Thread> GetHighestPriorityReadyThread();
-
- /// Get a const reference to the waiting threads list for debug use
- const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const;
-
-private:
- /// Threads waiting for this object to become available
- std::vector<SharedPtr<Thread>> waiting_threads;
-};
-
/**
- * 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.
+ * Attempts to downcast the given Object pointer to a pointer to T.
+ * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T.
*/
-class HandleTable final : NonCopyable {
-public:
- HandleTable();
-
- /**
- * 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(SharedPtr<Object> obj);
-
- /**
- * 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.
- */
- ResultCode Close(Handle handle);
-
- /// Checks if a handle is valid and points to an existing object.
- bool IsValid(Handle handle) const;
-
- /**
- * Looks up a handle.
- * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid.
- */
- SharedPtr<Object> GetGeneric(Handle handle) const;
-
- /**
- * Looks up a handle while verifying its type.
- * @return 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>
- SharedPtr<T> Get(Handle handle) const {
- SharedPtr<Object> object = GetGeneric(handle);
- if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
- return boost::static_pointer_cast<T>(std::move(object));
- }
- return nullptr;
- }
-
- /**
- * Looks up a handle while verifying that it is an object that a thread can wait on
- * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is
- * not a waitable object.
- */
- SharedPtr<WaitObject> GetWaitObject(Handle handle) const {
- SharedPtr<Object> object = GetGeneric(handle);
- if (object != nullptr && object->IsWaitable()) {
- return boost::static_pointer_cast<WaitObject>(std::move(object));
- }
- return nullptr;
- }
-
- /// Closes all handles held in this table.
- void Clear();
-
-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 u16 GetSlot(Handle handle) {
- return handle >> 15;
- }
- static u16 GetGeneration(Handle handle) {
- return handle & 0x7FFF;
+template <typename T>
+inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
+ if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
+ return boost::static_pointer_cast<T>(std::move(object));
}
-
- /// Stores the Object referenced by the handle or null if the slot is empty.
- std::array<SharedPtr<Object>, 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;
-
- /**
- * 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;
-
- /// Head of the free slots linked list.
- u16 next_free_slot;
-};
-
-extern HandleTable g_handle_table;
+ return nullptr;
+}
/// Initialize the kernel with the specified system mode.
void Init(u32 system_mode);
diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp
index 8250a90b5..804f23b1c 100644
--- a/src/core/hle/kernel/memory.cpp
+++ b/src/core/hle/kernel/memory.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <algorithm>
#include <cinttypes>
#include <map>
#include <memory>
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index c57adf400..bacacd690 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -7,6 +7,7 @@
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/wait_object.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 32cb25fb7..1c31ec950 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -6,6 +6,7 @@
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index 3f51bc5de..a8f10a3ee 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <cstring>
+#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/resource_limit.h"
diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp
index 8bda2f75d..fcf586728 100644
--- a/src/core/hle/kernel/semaphore.cpp
+++ b/src/core/hle/kernel/semaphore.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "common/assert.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/semaphore.h"
#include "core/hle/kernel/thread.h"
@@ -16,8 +17,7 @@ ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_cou
std::string name) {
if (initial_count > max_count)
- return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel,
- ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+ return ERR_INVALID_COMBINATION_KERNEL;
SharedPtr<Semaphore> semaphore(new Semaphore);
@@ -42,8 +42,7 @@ void Semaphore::Acquire(Thread* thread) {
ResultVal<s32> Semaphore::Release(s32 release_count) {
if (max_count - available_count < release_count)
- return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel,
- ErrorSummary::InvalidArgument, ErrorLevel::Permanent);
+ return ERR_OUT_OF_RANGE_KERNEL;
s32 previous_count = available_count;
available_count += release_count;
diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h
index cde94f7cc..7b0cacf2e 100644
--- a/src/core/hle/kernel/semaphore.h
+++ b/src/core/hle/kernel/semaphore.h
@@ -8,6 +8,8 @@
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/wait_object.h"
+#include "core/hle/result.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index 6f8bdb6a9..2a24d8412 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -9,6 +9,7 @@
#include <tuple>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/wait_object.h"
namespace Service {
class SessionRequestHandler;
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index c907d487c..f1b76d8aa 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -10,7 +10,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/session.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
@@ -20,6 +20,7 @@ namespace Kernel {
class ClientSession;
class ClientPort;
class ServerSession;
+class Thread;
/**
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index bc1560d12..922e5ab58 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -4,6 +4,7 @@
#include <cstring>
#include "common/logging/log.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
@@ -102,24 +103,21 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
- return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
- ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ return ERR_INVALID_COMBINATION;
}
// Error out if the requested permissions don't match what the creator process allows.
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
- return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
- ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ return ERR_INVALID_COMBINATION;
}
// Heap-backed memory blocks can not be mapped with other_permissions = DontCare
if (base_address != 0 && other_permissions == MemoryPermission::DontCare) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
- return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
- ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ return ERR_INVALID_COMBINATION;
}
// Error out if the provided permissions are not compatible with what the creator process needs.
@@ -127,8 +125,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, permissions don't match",
GetObjectId(), address, name.c_str());
- return ResultCode(ErrorDescription::WrongPermission, ErrorModule::OS,
- ErrorSummary::WrongArgument, ErrorLevel::Permanent);
+ return ERR_WRONG_PERMISSION;
}
// TODO(Subv): Check for the Shared Device Mem flag in the creator process.
@@ -144,8 +141,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
if (address < Memory::HEAP_VADDR || address + size >= Memory::SHARED_MEMORY_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X name=%s, invalid address",
GetObjectId(), address, name.c_str());
- return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
- ErrorSummary::InvalidArgument, ErrorLevel::Usage);
+ return ERR_INVALID_ADDRESS;
}
}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 3b7555d87..75ce626f8 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -14,6 +14,8 @@
#include "core/arm/skyeye_common/armstate.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory.h"
#include "core/hle/kernel/mutex.h"
@@ -241,9 +243,7 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
for (auto& object : thread->wait_objects)
object->RemoveWaitingThread(thread.get());
thread->wait_objects.clear();
- thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS,
- ErrorSummary::StatusChanged,
- ErrorLevel::Info));
+ thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
}
thread->ResumeFromWait();
@@ -351,10 +351,20 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_
context.cpsr = USER32MODE | ((entry_point & 1) << 5); // Usermode and THUMB mode
}
-ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority,
+ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, u32 priority,
u32 arg, s32 processor_id, VAddr stack_top) {
- ASSERT_MSG(priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST,
- "Invalid thread priority");
+ // Check if priority is in ranged. Lowest priority -> highest priority id.
+ if (priority > THREADPRIO_LOWEST) {
+ LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority);
+ return ERR_OUT_OF_RANGE;
+ }
+
+ if (processor_id > THREADPROCESSORID_MAX) {
+ LOG_ERROR(Kernel_SVC, "Invalid processor id: %d", processor_id);
+ return ERR_OUT_OF_RANGE_KERNEL;
+ }
+
+ // TODO(yuriks): Other checks, returning 0xD9001BEA
if (!Memory::IsValidVirtualAddress(entry_point)) {
LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point);
@@ -399,8 +409,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) {
LOG_ERROR(Kernel_SVC,
"Not enough space in region to allocate a new TLS page for thread");
- return ResultCode(ErrorDescription::OutOfMemory, ErrorModule::Kernel,
- ErrorSummary::OutOfResource, ErrorLevel::Permanent);
+ return ERR_OUT_OF_MEMORY;
}
u32 offset = linheap_memory->size();
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 6ab31c70b..6a3566f15 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -12,6 +12,7 @@
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
enum ThreadPriority : s32 {
@@ -57,7 +58,7 @@ public:
* @param stack_top The address of the thread's stack top
* @return A shared pointer to the newly created thread
*/
- static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority,
+ static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, u32 priority,
u32 arg, s32 processor_id, VAddr stack_top);
std::string GetName() const override {
diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp
index a00c75679..6f2cf3b02 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -6,6 +6,7 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core_timing.h"
+#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/timer.h"
diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h
index b0f818933..82552372d 100644
--- a/src/core/hle/kernel/timer.h
+++ b/src/core/hle/kernel/timer.h
@@ -6,6 +6,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/wait_object.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 6dd24f846..cef1f7fa8 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -4,6 +4,7 @@
#include <iterator>
#include "common/assert.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
#include "core/memory_setup.h"
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 9055664b2..38e0d74d0 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -13,14 +13,6 @@
namespace Kernel {
-const ResultCode ERR_INVALID_ADDRESS{// 0xE0E01BF5
- ErrorDescription::InvalidAddress, ErrorModule::OS,
- ErrorSummary::InvalidArgument, ErrorLevel::Usage};
-
-const ResultCode ERR_INVALID_ADDRESS_STATE{// 0xE0A01BF5
- ErrorDescription::InvalidAddress, ErrorModule::OS,
- ErrorSummary::InvalidState, ErrorLevel::Usage};
-
enum class VMAType : u8 {
/// VMA represents an unmapped region of the address space.
Free,
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
new file mode 100644
index 000000000..f245eda6c
--- /dev/null
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -0,0 +1,99 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/hle/config_mem.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/memory.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/timer.h"
+#include "core/hle/shared_page.h"
+
+namespace Kernel {
+
+void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) {
+ auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
+ if (itr == waiting_threads.end())
+ waiting_threads.push_back(std::move(thread));
+}
+
+void WaitObject::RemoveWaitingThread(Thread* thread) {
+ auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread);
+ // If a thread passed multiple handles to the same object,
+ // the kernel might attempt to remove the thread from the object's
+ // waiting threads list multiple times.
+ if (itr != waiting_threads.end())
+ waiting_threads.erase(itr);
+}
+
+SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
+ Thread* candidate = nullptr;
+ s32 candidate_priority = THREADPRIO_LOWEST + 1;
+
+ for (const auto& thread : waiting_threads) {
+ // The list of waiting threads must not contain threads that are not waiting to be awakened.
+ ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
+ thread->status == THREADSTATUS_WAIT_SYNCH_ALL,
+ "Inconsistent thread statuses in waiting_threads");
+
+ if (thread->current_priority >= candidate_priority)
+ continue;
+
+ if (ShouldWait(thread.get()))
+ continue;
+
+ // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or
+ // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready.
+ bool ready_to_run = true;
+ if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) {
+ ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
+ [&thread](const SharedPtr<WaitObject>& object) {
+ return object->ShouldWait(thread.get());
+ });
+ }
+
+ if (ready_to_run) {
+ candidate = thread.get();
+ candidate_priority = thread->current_priority;
+ }
+ }
+
+ return candidate;
+}
+
+void WaitObject::WakeupAllWaitingThreads() {
+ while (auto thread = GetHighestPriorityReadyThread()) {
+ if (!thread->IsSleepingOnWaitAll()) {
+ Acquire(thread.get());
+ // Set the output index of the WaitSynchronizationN call to the index of this object.
+ if (thread->wait_set_output) {
+ thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this));
+ thread->wait_set_output = false;
+ }
+ } else {
+ for (auto& object : thread->wait_objects) {
+ object->Acquire(thread.get());
+ }
+ // Note: This case doesn't update the output index of WaitSynchronizationN.
+ }
+
+ for (auto& object : thread->wait_objects)
+ object->RemoveWaitingThread(thread.get());
+ thread->wait_objects.clear();
+
+ thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
+ thread->ResumeFromWait();
+ }
+}
+
+const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const {
+ return waiting_threads;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h
new file mode 100644
index 000000000..861578186
--- /dev/null
+++ b/src/core/hle/kernel/wait_object.h
@@ -0,0 +1,67 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include <boost/smart_ptr/intrusive_ptr.hpp>
+#include "common/common_types.h"
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+class Thread;
+
+/// Class that represents a Kernel object that a thread can be waiting on
+class WaitObject : public Object {
+public:
+ /**
+ * Check if the specified thread should wait until the object is available
+ * @param thread The thread about which we're deciding.
+ * @return True if the current thread should wait due to this object being unavailable
+ */
+ virtual bool ShouldWait(Thread* thread) const = 0;
+
+ /// Acquire/lock the object for the specified thread if it is available
+ virtual void Acquire(Thread* thread) = 0;
+
+ /**
+ * Add a thread to wait on this object
+ * @param thread Pointer to thread to add
+ */
+ virtual void AddWaitingThread(SharedPtr<Thread> thread);
+
+ /**
+ * Removes a thread from waiting on this object (e.g. if it was resumed already)
+ * @param thread Pointer to thread to remove
+ */
+ virtual void RemoveWaitingThread(Thread* thread);
+
+ /**
+ * Wake up all threads waiting on this object that can be awoken, in priority order,
+ * and set the synchronization result and output of the thread.
+ */
+ virtual void WakeupAllWaitingThreads();
+
+ /// Obtains the highest priority thread that is ready to run from this object's waiting list.
+ SharedPtr<Thread> GetHighestPriorityReadyThread();
+
+ /// Get a const reference to the waiting threads list for debug use
+ const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const;
+
+private:
+ /// Threads waiting for this object to become available
+ std::vector<SharedPtr<Thread>> waiting_threads;
+};
+
+// Specialization of DynamicObjectCast for WaitObjects
+template <>
+inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) {
+ if (object != nullptr && object->IsWaitable()) {
+ return boost::static_pointer_cast<WaitObject>(std::move(object));
+ }
+ return nullptr;
+}
+
+} // namespace Kernel