diff options
Diffstat (limited to 'src/core/hle')
36 files changed, 1090 insertions, 494 deletions
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 0f822f84b..a2f51b41b 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -5,6 +5,8 @@ #pragma once #include "common/common_types.h" + +#include "core/arm/arm_interface.h" #include "core/mem_map.h" #include "core/hle/hle.h" @@ -135,6 +137,12 @@ template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() { FuncReturn(retval); } +template<s32 func(u32, s64, s64)> void Wrap() { + s64 param1 = ((u64)PARAM(3) << 32) | PARAM(2); + s64 param2 = ((u64)PARAM(4) << 32) | PARAM(1); + FuncReturn(func(PARAM(0), param1, param2)); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Function wrappers that return type u32 diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index f76048d14..11570c8b4 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -4,6 +4,7 @@ #include <vector> +#include "core/arm/arm_interface.h" #include "core/mem_map.h" #include "core/hle/hle.h" #include "core/hle/shared_page.h" diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h index 59b770f02..3f6f9a4b5 100644 --- a/src/core/hle/hle.h +++ b/src/core/hle/hle.h @@ -4,6 +4,8 @@ #pragma once +#include <string> + #include "common/common_types.h" #include "core/core.h" diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 736bbc36a..b7434aaf2 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -29,35 +29,56 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////// /// Arbitrate an address -ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { +ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds) { + Object* object = Kernel::g_handle_table.GetGeneric(handle).get(); + if (object == nullptr) + return InvalidHandle(ErrorModule::Kernel); + switch (type) { // Signal thread(s) waiting for arbitrate address... case ArbitrationType::Signal: // Negative value means resume all threads if (value < 0) { - ArbitrateAllThreads(handle, address); + ArbitrateAllThreads(object, address); } else { // Resume first N threads for(int i = 0; i < value; i++) - ArbitrateHighestPriorityThread(handle, address); + ArbitrateHighestPriorityThread(object, address); } break; // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); + Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + HLE::Reschedule(__func__); + } + break; + case ArbitrationType::WaitIfLessThanWithTimeout: + if ((s32)Memory::Read32(address) <= value) { + Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); HLE::Reschedule(__func__); } break; - case ArbitrationType::DecrementAndWaitIfLessThan: { s32 memory_value = Memory::Read32(address) - 1; Memory::Write32(address, memory_value); if (memory_value <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); + Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + HLE::Reschedule(__func__); + } + break; + } + case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: + { + s32 memory_value = Memory::Read32(address) - 1; + Memory::Write32(address, memory_value); + if (memory_value <= value) { + Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WakeThreadAfterDelay(GetCurrentThread(), nanoseconds); HLE::Reschedule(__func__); } break; diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 030e7ad7b..3ffd465a2 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -28,7 +28,7 @@ enum class ArbitrationType : u32 { }; /// Arbitrate an address -ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value); +ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value, u64 nanoseconds); /// Create an address arbiter Handle CreateAddressArbiter(const std::string& name = "Unknown"); diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index e43c3ee4e..271190dbe 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -33,11 +33,11 @@ public: ResultVal<bool> WaitSynchronization() override { bool wait = locked; if (locked) { - Handle thread = GetCurrentThreadHandle(); + Handle thread = GetCurrentThread()->GetHandle(); if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { waiting_threads.push_back(thread); } - Kernel::WaitCurrentThread(WAITTYPE_EVENT, GetHandle()); + Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); } if (reset_type != RESETTYPE_STICKY && !permanent_locked) { locked = true; @@ -53,7 +53,7 @@ public: * @return Result of operation, 0 on success, otherwise error code */ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { - Event* evt = g_handle_table.Get<Event>(handle); + Event* evt = g_handle_table.Get<Event>(handle).get(); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); evt->permanent_locked = permanent_locked; @@ -67,7 +67,7 @@ ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { * @return Result of operation, 0 on success, otherwise error code */ ResultCode SetEventLocked(const Handle handle, const bool locked) { - Event* evt = g_handle_table.Get<Event>(handle); + Event* evt = g_handle_table.Get<Event>(handle).get(); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!evt->permanent_locked) { @@ -82,13 +82,15 @@ ResultCode SetEventLocked(const Handle handle, const bool locked) { * @return Result of operation, 0 on success, otherwise error code */ ResultCode SignalEvent(const Handle handle) { - Event* evt = g_handle_table.Get<Event>(handle); + Event* evt = g_handle_table.Get<Event>(handle).get(); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); // Resume threads waiting for event to signal bool event_caught = false; for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { - ResumeThreadFromWait( evt->waiting_threads[i]); + Thread* thread = Kernel::g_handle_table.Get<Thread>(evt->waiting_threads[i]).get(); + if (thread != nullptr) + thread->ResumeFromWait(); // If any thread is signalled awake by this event, assume the event was "caught" and reset // the event. This will result in the next thread waiting on the event to block. Otherwise, @@ -110,7 +112,7 @@ ResultCode SignalEvent(const Handle handle) { * @return Result of operation, 0 on success, otherwise error code */ ResultCode ClearEvent(Handle handle) { - Event* evt = g_handle_table.Get<Event>(handle); + Event* evt = g_handle_table.Get<Event>(handle).get(); if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!evt->permanent_locked) { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index ae2c11a1c..d3684896f 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -6,13 +6,15 @@ #include "common/common.h" +#include "core/arm/arm_interface.h" #include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/timer.h" namespace Kernel { -Handle g_main_thread = 0; +SharedPtr<Thread> g_main_thread = nullptr; HandleTable g_handle_table; u64 g_program_id = 0; @@ -21,7 +23,7 @@ HandleTable::HandleTable() { Clear(); } -ResultVal<Handle> HandleTable::Create(Object* obj) { +ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { _dbg_assert_(Kernel, obj != nullptr); u16 slot = next_free_slot; @@ -37,22 +39,23 @@ ResultVal<Handle> HandleTable::Create(Object* obj) { // CTR-OS doesn't use generation 0, so skip straight to 1. if (next_generation >= (1 << 15)) next_generation = 1; + Handle handle = generation | (slot << 15); + if (obj->handle == INVALID_HANDLE) + obj->handle = handle; + generations[slot] = generation; - intrusive_ptr_add_ref(obj); - objects[slot] = obj; + objects[slot] = std::move(obj); - Handle handle = generation | (slot << 15); - obj->handle = handle; return MakeResult<Handle>(handle); } ResultVal<Handle> HandleTable::Duplicate(Handle handle) { - Object* object = GetGeneric(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(object); + return Create(std::move(object)); } ResultCode HandleTable::Close(Handle handle) { @@ -62,7 +65,6 @@ ResultCode HandleTable::Close(Handle handle) { size_t slot = GetSlot(handle); u16 generation = GetGeneration(handle); - intrusive_ptr_release(objects[slot]); objects[slot] = nullptr; generations[generation] = next_free_slot; @@ -77,10 +79,9 @@ bool HandleTable::IsValid(Handle handle) const { return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; } -Object* HandleTable::GetGeneric(Handle handle) const { +SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { if (handle == CurrentThread) { - // TODO(yuriks) Directly return the pointer once this is possible. - handle = GetCurrentThreadHandle(); + return GetCurrentThread(); } else if (handle == CurrentProcess) { LOG_ERROR(Kernel, "Current process (%08X) pseudo-handle not supported", CurrentProcess); return nullptr; @@ -95,8 +96,6 @@ Object* HandleTable::GetGeneric(Handle handle) const { void HandleTable::Clear() { for (size_t i = 0; i < MAX_COUNT; ++i) { generations[i] = i + 1; - if (objects[i] != nullptr) - intrusive_ptr_release(objects[i]); objects[i] = nullptr; } next_free_slot = 0; @@ -105,12 +104,13 @@ void HandleTable::Clear() { /// Initialize the kernel void Init() { Kernel::ThreadingInit(); + Kernel::TimersInit(); } /// Shutdown the kernel void Shutdown() { Kernel::ThreadingShutdown(); - + Kernel::TimersShutdown(); g_handle_table.Clear(); // Free all kernel objects } @@ -123,7 +123,7 @@ bool LoadExec(u32 entry_point) { Core::g_app_core->SetPC(entry_point); // 0x30 is the typical main thread priority I've seen used so far - g_main_thread = Kernel::SetupMainThread(0x30); + g_main_thread = Kernel::SetupMainThread(0x30, Kernel::DEFAULT_STACK_SIZE); // Setup the idle thread Kernel::SetupIdleThread(); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 7f86fd07d..5e5217b78 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,6 +4,8 @@ #pragma once +#include <boost/intrusive_ptr.hpp> + #include <array> #include <string> #include "common/common.h" @@ -16,6 +18,8 @@ const Handle INVALID_HANDLE = 0; namespace Kernel { +class Thread; + // TODO: Verify code const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, ErrorSummary::OutOfResource, ErrorLevel::Temporary); @@ -39,6 +43,7 @@ enum class HandleType : u32 { Process = 8, AddressArbiter = 9, Semaphore = 10, + Timer = 11 }; enum { @@ -49,7 +54,7 @@ class HandleTable; class Object : NonCopyable { friend class HandleTable; - u32 handle; + u32 handle = INVALID_HANDLE; public: virtual ~Object() {} Handle GetHandle() const { return handle; } @@ -73,7 +78,7 @@ private: unsigned int ref_count = 0; }; -// Special functions that will later be used by boost::instrusive_ptr to do automatic ref-counting +// Special functions used by boost::instrusive_ptr to do automatic ref-counting inline void intrusive_ptr_add_ref(Object* object) { ++object->ref_count; } @@ -84,6 +89,9 @@ inline void intrusive_ptr_release(Object* object) { } } +template <typename T> +using SharedPtr = boost::intrusive_ptr<T>; + /** * 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 @@ -116,7 +124,7 @@ public: * @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); + ResultVal<Handle> Create(SharedPtr<Object> obj); /** * Returns a new handle that points to the same object as the passed in handle. @@ -140,7 +148,7 @@ public: * Looks up a handle. * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. */ - Object* GetGeneric(Handle handle) const; + SharedPtr<Object> GetGeneric(Handle handle) const; /** * Looks up a handle while verifying its type. @@ -148,10 +156,10 @@ public: * type differs from the handle type `T::HANDLE_TYPE`. */ template <class T> - T* Get(Handle handle) const { - Object* object = GetGeneric(handle); + SharedPtr<T> Get(Handle handle) const { + SharedPtr<Object> object = GetGeneric(handle); if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { - return static_cast<T*>(object); + return boost::static_pointer_cast<T>(std::move(object)); } return nullptr; } @@ -170,7 +178,7 @@ private: 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; + std::array<SharedPtr<Object>, MAX_COUNT> objects; /** * The value of `next_generation` when the handle was created, used to check for validity. For @@ -189,7 +197,7 @@ private: }; extern HandleTable g_handle_table; -extern Handle g_main_thread; +extern SharedPtr<Thread> g_main_thread; /// The ID code of the currently running game /// TODO(Subv): This variable should not be here, diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 3dfeffc9b..853a5dd74 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -40,14 +40,21 @@ static MutexMap g_mutex_held_locks; * @param mutex Mutex that is to be acquired * @param thread Thread that will acquired */ -void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { +void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) { g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); mutex->lock_thread = thread; } -bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { - MutexAcquireLock(mutex, thread); - Kernel::ResumeThreadFromWait(thread); +bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { + MutexAcquireLock(mutex, thread_handle); + + Thread* thread = Kernel::g_handle_table.Get<Thread>(thread_handle).get(); + if (thread == nullptr) { + LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle); + return false; + } + + thread->ResumeFromWait(); return true; } @@ -87,7 +94,7 @@ void ReleaseThreadMutexes(Handle thread) { // Release every mutex that the thread holds, and resume execution on the waiting threads for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - Mutex* mutex = g_handle_table.Get<Mutex>(iter->second); + Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get(); ResumeWaitingThread(mutex); } @@ -115,7 +122,7 @@ bool ReleaseMutex(Mutex* mutex) { * @param handle Handle to mutex to release */ ResultCode ReleaseMutex(Handle handle) { - Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle); + Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle).get(); if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!ReleaseMutex(mutex)) { @@ -168,8 +175,8 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { ResultVal<bool> Mutex::WaitSynchronization() { bool wait = locked; if (locked) { - waiting_threads.push_back(GetCurrentThreadHandle()); - Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); + waiting_threads.push_back(GetCurrentThread()->GetHandle()); + Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); } else { // Lock the mutex when the first thread accesses it locked = true; diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 6bc8066a6..88ec9a104 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -37,8 +37,8 @@ public: bool wait = !IsAvailable(); if (wait) { - Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle()); - waiting_threads.push(GetCurrentThreadHandle()); + Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); + waiting_threads.push(GetCurrentThread()->GetHandle()); } else { --available_count; } @@ -70,7 +70,7 @@ ResultCode CreateSemaphore(Handle* handle, s32 initial_count, } ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { - Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle); + Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle).get(); if (semaphore == nullptr) return InvalidHandle(ErrorModule::Kernel); @@ -84,7 +84,9 @@ ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { // Notify some of the threads that the semaphore has been released // stop once the semaphore is full again or there are no more waiting threads while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { - Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front()); + Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get(); + if (thread != nullptr) + thread->ResumeFromWait(); semaphore->waiting_threads.pop(); --semaphore->available_count; } diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index cea1f6fa1..5368e4728 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -61,7 +61,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } - SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); + SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get(); if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); shared_memory->base_address = address; @@ -72,7 +72,7 @@ ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions } ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { - SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); + SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get(); if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); if (0 != shared_memory->base_address) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 58fb62e89..bc86a7c59 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -10,6 +10,7 @@ #include "common/common.h" #include "common/thread_queue_list.h" +#include "core/arm/arm_interface.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/hle.h" @@ -21,68 +22,25 @@ namespace Kernel { -class Thread : public Kernel::Object { -public: - - std::string GetName() const override { return name; } - std::string GetTypeName() const override { return "Thread"; } - - static const HandleType HANDLE_TYPE = HandleType::Thread; - HandleType GetHandleType() const override { return HANDLE_TYPE; } - - inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } - inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } - inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } - inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } - inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } - inline bool IsIdle() const { return idle; } - - ResultVal<bool> WaitSynchronization() override { - const bool wait = status != THREADSTATUS_DORMANT; - if (wait) { - Handle thread = GetCurrentThreadHandle(); - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { - waiting_threads.push_back(thread); - } - WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); +ResultVal<bool> Thread::WaitSynchronization() { + const bool wait = status != THREADSTATUS_DORMANT; + if (wait) { + Thread* thread = GetCurrentThread(); + if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { + waiting_threads.push_back(thread); } - - return MakeResult<bool>(wait); + WaitCurrentThread(WAITTYPE_THREADEND, this); } - ThreadContext context; - - u32 thread_id; - - u32 status; - u32 entry_point; - u32 stack_top; - u32 stack_size; - - s32 initial_priority; - s32 current_priority; - - s32 processor_id; - - WaitType wait_type; - Handle wait_handle; - VAddr wait_address; - - std::vector<Handle> waiting_threads; - - std::string name; - - /// Whether this thread is intended to never actually be executed, i.e. always idle - bool idle = false; -}; + return MakeResult<bool>(wait); +} // Lists all thread ids that aren't deleted/etc. -static std::vector<Handle> thread_queue; +static std::vector<SharedPtr<Thread>> thread_list; // Lists only ready thread ids. -static Common::ThreadQueueList<Handle, THREADPRIO_LOWEST+1> thread_ready_queue; +static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST+1> thread_ready_queue; -static Handle current_thread_handle; static Thread* current_thread; static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup @@ -92,30 +50,9 @@ Thread* GetCurrentThread() { return current_thread; } -/// Gets the current thread handle -Handle GetCurrentThreadHandle() { - return GetCurrentThread()->GetHandle(); -} - -/// Sets the current thread -inline void SetCurrentThread(Thread* t) { - current_thread = t; - current_thread_handle = t->GetHandle(); -} - -/// Saves the current CPU context -void SaveContext(ThreadContext& ctx) { - Core::g_app_core->SaveContext(ctx); -} - -/// Loads a CPU context -void LoadContext(ThreadContext& ctx) { - Core::g_app_core->LoadContext(ctx); -} - /// Resets a thread -void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { - memset(&t->context, 0, sizeof(ThreadContext)); +static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { + memset(&t->context, 0, sizeof(Core::ThreadContext)); t->context.cpu_registers[0] = arg; t->context.pc = t->context.reg_15 = t->entry_point; @@ -131,22 +68,21 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { t->current_priority = t->initial_priority; } t->wait_type = WAITTYPE_NONE; - t->wait_handle = 0; + t->wait_object = nullptr; t->wait_address = 0; } /// Change a thread to "ready" state -void ChangeReadyState(Thread* t, bool ready) { - Handle handle = t->GetHandle(); +static void ChangeReadyState(Thread* t, bool ready) { if (t->IsReady()) { if (!ready) { - thread_ready_queue.remove(t->current_priority, handle); + thread_ready_queue.remove(t->current_priority, t); } } else if (ready) { if (t->IsRunning()) { - thread_ready_queue.push_front(t->current_priority, handle); + thread_ready_queue.push_front(t->current_priority, t); } else { - thread_ready_queue.push_back(t->current_priority, handle); + thread_ready_queue.push_back(t->current_priority, t); } t->status = THREADSTATUS_READY; } @@ -158,43 +94,36 @@ static bool CheckWaitType(const Thread* thread, WaitType type) { } /// Check if a thread is blocking on a specified wait type with a specified handle -static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) { - return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle); +static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { + return CheckWaitType(thread, type) && wait_object == thread->wait_object; } /// Check if a thread is blocking on a specified wait type with a specified handle and address -static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { - return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address); +static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { + return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); } /// Stops the current thread -ResultCode StopThread(Handle handle, const char* reason) { - Thread* thread = g_handle_table.Get<Thread>(handle); - if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); - +void Thread::Stop(const char* reason) { // Release all the mutexes that this thread holds - ReleaseThreadMutexes(handle); - - ChangeReadyState(thread, false); - thread->status = THREADSTATUS_DORMANT; - for (Handle waiting_handle : thread->waiting_threads) { - Thread* waiting_thread = g_handle_table.Get<Thread>(waiting_handle); + ReleaseThreadMutexes(GetHandle()); - if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle)) - ResumeThreadFromWait(waiting_handle); + ChangeReadyState(this, false); + status = THREADSTATUS_DORMANT; + for (auto& waiting_thread : waiting_threads) { + if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this)) + waiting_thread->ResumeFromWait(); } - thread->waiting_threads.clear(); + waiting_threads.clear(); // Stopped threads are never waiting. - thread->wait_type = WAITTYPE_NONE; - thread->wait_handle = 0; - thread->wait_address = 0; - - return RESULT_SUCCESS; + wait_type = WAITTYPE_NONE; + wait_object = nullptr; + wait_address = 0; } /// Changes a threads state -void ChangeThreadState(Thread* t, ThreadStatus new_status) { +static void ChangeThreadState(Thread* t, ThreadStatus new_status) { if (!t || t->status == new_status) { return; } @@ -209,46 +138,44 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { } /// Arbitrate the highest priority thread that is waiting -Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { - Handle highest_priority_thread = 0; +Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { + Thread* highest_priority_thread = nullptr; s32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (Handle handle : thread_queue) { - Thread* thread = g_handle_table.Get<Thread>(handle); - - if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) + for (auto& thread : thread_list) { + if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) continue; if (thread == nullptr) continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. if(thread->current_priority <= priority) { - highest_priority_thread = handle; + highest_priority_thread = thread.get(); priority = thread->current_priority; } } + // If a thread was arbitrated, resume it - if (0 != highest_priority_thread) - ResumeThreadFromWait(highest_priority_thread); + if (nullptr != highest_priority_thread) { + highest_priority_thread->ResumeFromWait(); + } return highest_priority_thread; } /// Arbitrate all threads currently waiting -void ArbitrateAllThreads(u32 arbiter, u32 address) { +void ArbitrateAllThreads(Object* arbiter, u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (Handle handle : thread_queue) { - Thread* thread = g_handle_table.Get<Thread>(handle); - - if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address)) - ResumeThreadFromWait(handle); + for (auto& thread : thread_list) { + if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) + thread->ResumeFromWait(); } } /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void CallThread(Thread* t) { +static void CallThread(Thread* t) { // Stop waiting if (t->wait_type != WAITTYPE_NONE) { t->wait_type = WAITTYPE_NONE; @@ -257,12 +184,12 @@ void CallThread(Thread* t) { } /// Switches CPU context to that of the specified thread -void SwitchContext(Thread* t) { +static void SwitchContext(Thread* t) { Thread* cur = GetCurrentThread(); // Save context for current thread if (cur) { - SaveContext(cur->context); + Core::g_app_core->SaveContext(cur->context); if (cur->IsRunning()) { ChangeReadyState(cur, true); @@ -270,19 +197,19 @@ void SwitchContext(Thread* t) { } // Load context of new thread if (t) { - SetCurrentThread(t); + current_thread = t; ChangeReadyState(t, false); t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; t->wait_type = WAITTYPE_NONE; - LoadContext(t->context); + Core::g_app_core->LoadContext(t->context); } else { - SetCurrentThread(nullptr); + current_thread = nullptr; } } /// Gets the next thread that is ready to be run by priority -Thread* NextThread() { - Handle next; +static Thread* NextThread() { + Thread* next; Thread* cur = GetCurrentThread(); if (cur && cur->IsRunning()) { @@ -293,63 +220,111 @@ Thread* NextThread() { if (next == 0) { return nullptr; } - return Kernel::g_handle_table.Get<Thread>(next); + return next; } -void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { +void WaitCurrentThread(WaitType wait_type, Object* wait_object) { Thread* thread = GetCurrentThread(); thread->wait_type = wait_type; - thread->wait_handle = wait_handle; + thread->wait_object = wait_object; ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { - WaitCurrentThread(wait_type, wait_handle); +void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { + WaitCurrentThread(wait_type, wait_object); GetCurrentThread()->wait_address = wait_address; } +/// Event type for the thread wake up event +static int ThreadWakeupEventType = -1; + +/// Callback that will wake up the thread it was scheduled for +static void ThreadWakeupCallback(u64 parameter, int cycles_late) { + Handle handle = static_cast<Handle>(parameter); + SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle); + if (thread == nullptr) { + LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); + return; + } + + thread->ResumeFromWait(); +} + + +void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { + // Don't schedule a wakeup if the thread wants to wait forever + if (nanoseconds == -1) + return; + _dbg_assert_(Kernel, thread != nullptr); + + u64 microseconds = nanoseconds / 1000; + CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); +} + /// Resumes a thread from waiting by marking it as "ready" -void ResumeThreadFromWait(Handle handle) { - Thread* thread = Kernel::g_handle_table.Get<Thread>(handle); - if (thread) { - thread->status &= ~THREADSTATUS_WAIT; - thread->wait_handle = 0; - thread->wait_type = WAITTYPE_NONE; - if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { - ChangeReadyState(thread, true); - } +void Thread::ResumeFromWait() { + // Cancel any outstanding wakeup events + CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); + + status &= ~THREADSTATUS_WAIT; + wait_object = nullptr; + wait_type = WAITTYPE_NONE; + if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { + ChangeReadyState(this, true); } } /// Prints the thread queue for debugging purposes -void DebugThreadQueue() { +static void DebugThreadQueue() { Thread* thread = GetCurrentThread(); if (!thread) { return; } - LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); - for (u32 i = 0; i < thread_queue.size(); i++) { - Handle handle = thread_queue[i]; - s32 priority = thread_ready_queue.contains(handle); + LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle()); + for (auto& t : thread_list) { + s32 priority = thread_ready_queue.contains(t.get()); if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); + LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle()); } } } -/// Creates a new thread -Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, - s32 processor_id, u32 stack_top, int stack_size) { +ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, s32 priority, + u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size) { + if (stack_size < 0x200) { + LOG_ERROR(Kernel, "(name=%s): invalid stack_size=0x%08X", name.c_str(), stack_size); + // TODO: Verify error + return ResultCode(ErrorDescription::InvalidSize, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } - _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), - "priority=%d, outside of allowable range!", priority) + if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { + s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); + LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", + name.c_str(), priority, new_priority); + // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm + // validity of this + priority = new_priority; + } + + if (!Memory::GetPointer(entry_point)) { + LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); + // TODO: Verify error + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } - Thread* thread = new Thread; + SharedPtr<Thread> thread(new Thread); - // TOOD(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(thread).ValueOr(INVALID_HANDLE); + // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for + // the time being. Create a handle here, it will be copied to the handle field in + // the object and use by the rest of the code. This should be removed when other + // code doesn't rely on the handle anymore. + ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread); + if (handle.Failed()) + return handle.Code(); - thread_queue.push_back(handle); + thread_list.push_back(thread); thread_ready_queue.prepare(priority); thread->thread_id = next_thread_id++; @@ -360,69 +335,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio thread->initial_priority = thread->current_priority = priority; thread->processor_id = processor_id; thread->wait_type = WAITTYPE_NONE; - thread->wait_handle = 0; + thread->wait_object = nullptr; thread->wait_address = 0; - thread->name = name; - - return thread; -} - -/// Creates a new thread - wrapper for external user -Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, - u32 stack_top, int stack_size) { - - if (name == nullptr) { - LOG_ERROR(Kernel_SVC, "nullptr name"); - return -1; - } - if ((u32)stack_size < 0x200) { - LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name, - stack_size); - return -1; - } - if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { - s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); - LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", - name, priority, new_priority); - // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm - // validity of this - priority = new_priority; - } - if (!Memory::GetPointer(entry_point)) { - LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); - return -1; - } - Handle handle; - Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, - stack_size); + thread->name = std::move(name); - ResetThread(thread, arg, 0); - CallThread(thread); + ResetThread(thread.get(), arg, 0); + CallThread(thread.get()); - return handle; -} - -/// Get the priority of the thread specified by handle -ResultVal<u32> GetThreadPriority(const Handle handle) { - Thread* thread = g_handle_table.Get<Thread>(handle); - if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); - - return MakeResult<u32>(thread->current_priority); + return MakeResult<SharedPtr<Thread>>(std::move(thread)); } /// Set the priority of the thread specified by handle -ResultCode SetThreadPriority(Handle handle, s32 priority) { - Thread* thread = nullptr; - if (!handle) { - thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? - } else { - thread = g_handle_table.Get<Thread>(handle); - if (thread == nullptr) { - return InvalidHandle(ErrorModule::Kernel); - } - } - _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); - +void Thread::SetPriority(s32 priority) { // If priority is invalid, clamp to valid range if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); @@ -433,38 +357,39 @@ ResultCode SetThreadPriority(Handle handle, s32 priority) { } // Change thread priority - s32 old = thread->current_priority; - thread_ready_queue.remove(old, handle); - thread->current_priority = priority; - thread_ready_queue.prepare(thread->current_priority); + s32 old = current_priority; + thread_ready_queue.remove(old, this); + current_priority = priority; + thread_ready_queue.prepare(current_priority); // Change thread status to "ready" and push to ready queue - if (thread->IsRunning()) { - thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; + if (IsRunning()) { + status = (status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; } - if (thread->IsReady()) { - thread_ready_queue.push_back(thread->current_priority, handle); + if (IsReady()) { + thread_ready_queue.push_back(current_priority, this); } - - return RESULT_SUCCESS; } Handle SetupIdleThread() { - Handle handle; - Thread* thread = CreateThread(handle, "idle", 0, THREADPRIO_LOWEST, THREADPROCESSORID_0, 0, 0); + // We need to pass a few valid values to get around parameter checking in Thread::Create. + auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, + THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE); + _dbg_assert_(Kernel, thread_res.Succeeded()); + SharedPtr<Thread> thread = std::move(*thread_res); + thread->idle = true; - CallThread(thread); - return handle; + CallThread(thread.get()); + return thread->GetHandle(); } -Handle SetupMainThread(s32 priority, int stack_size) { - Handle handle; - +SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { // Initialize new "main" thread - Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, - THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - - ResetThread(thread, 0, 0); + auto thread_res = Thread::Create("main", Core::g_app_core->GetPC(), priority, 0, + THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); + // TODO(yuriks): Propagate error + _dbg_assert_(Kernel, thread_res.Succeeded()); + SharedPtr<Thread> thread = std::move(*thread_res); // If running another thread already, set it to "ready" state Thread* cur = GetCurrentThread(); @@ -473,11 +398,11 @@ Handle SetupMainThread(s32 priority, int stack_size) { } // Run new "main" thread - SetCurrentThread(thread); + current_thread = thread.get(); thread->status = THREADSTATUS_RUNNING; - LoadContext(thread->context); + Core::g_app_core->LoadContext(thread->context); - return handle; + return thread; } @@ -493,46 +418,19 @@ void Reschedule() { } else { LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); - for (Handle handle : thread_queue) { - Thread* thread = g_handle_table.Get<Thread>(handle); + for (auto& thread : thread_list) { LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", - thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle); + thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, + (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); } } - - // TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put - // to sleep. So, we'll just immediately set it to "ready" again after an attempted context - // switch has occurred. This results in the current thread yielding on a sleep once, and then it - // will immediately be placed back in the queue for execution. - - if (CheckWaitType(prev, WAITTYPE_SLEEP)) - ResumeThreadFromWait(prev->GetHandle()); -} - -bool IsIdleThread(Handle handle) { - Thread* thread = g_handle_table.Get<Thread>(handle); - if (!thread) { - LOG_ERROR(Kernel, "Thread not found %u", handle); - return false; - } - return thread->IsIdle(); -} - -ResultCode GetThreadId(u32* thread_id, Handle handle) { - Thread* thread = g_handle_table.Get<Thread>(handle); - if (thread == nullptr) - return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent); - - *thread_id = thread->thread_id; - - return RESULT_SUCCESS; } //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { next_thread_id = INITIAL_THREAD_ID; + ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); } void ThreadingShutdown() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index dfe92d162..284dec400 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -4,8 +4,12 @@ #pragma once +#include <string> +#include <vector> + #include "common/common_types.h" +#include "core/core.h" #include "core/mem_map.h" #include "core/hle/kernel/kernel.h" @@ -43,66 +47,107 @@ enum WaitType { WAITTYPE_MUTEX, WAITTYPE_SYNCH, WAITTYPE_ARB, + WAITTYPE_TIMER, }; namespace Kernel { -/// Creates a new thread - wrapper for external user -Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, - u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); +class Thread : public Kernel::Object { +public: + static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, + u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); -/// Sets up the primary application thread -Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); + std::string GetName() const override { return name; } + std::string GetTypeName() const override { return "Thread"; } -/// Reschedules to the next available thread (call after current thread is suspended) -void Reschedule(); + static const HandleType HANDLE_TYPE = HandleType::Thread; + HandleType GetHandleType() const override { return HANDLE_TYPE; } -/// Stops the current thread -ResultCode StopThread(Handle thread, const char* reason); + inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } + inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } + inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; } + inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } + inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } + inline bool IsIdle() const { return idle; } -/** - * Retrieves the ID of the specified thread handle - * @param thread_id Will contain the output thread id - * @param handle Handle to the thread we want - * @return Whether the function was successful or not - */ -ResultCode GetThreadId(u32* thread_id, Handle handle); + ResultVal<bool> WaitSynchronization() override; + + s32 GetPriority() const { return current_priority; } + void SetPriority(s32 priority); + + u32 GetThreadId() const { return thread_id; } + + void Stop(const char* reason); + /// Resumes a thread from waiting by marking it as "ready". + void ResumeFromWait(); + + Core::ThreadContext context; + + u32 thread_id; + + u32 status; + u32 entry_point; + u32 stack_top; + u32 stack_size; + + s32 initial_priority; + s32 current_priority; + + s32 processor_id; + + WaitType wait_type; + Object* wait_object; + VAddr wait_address; -/// Resumes a thread from waiting by marking it as "ready" -void ResumeThreadFromWait(Handle handle); + std::vector<SharedPtr<Thread>> waiting_threads; + + std::string name; + + /// Whether this thread is intended to never actually be executed, i.e. always idle + bool idle = false; + +private: + Thread() = default; +}; + +/// Sets up the primary application thread +SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); + +/// Reschedules to the next available thread (call after current thread is suspended) +void Reschedule(); /// Arbitrate the highest priority thread that is waiting -Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address); +Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address); /// Arbitrate all threads currently waiting... -void ArbitrateAllThreads(u32 arbiter, u32 address); +void ArbitrateAllThreads(Object* arbiter, u32 address); -/// Gets the current thread handle -Handle GetCurrentThreadHandle(); +/// Gets the current thread +Thread* GetCurrentThread(); /** * Puts the current thread in the wait state for the given type * @param wait_type Type of wait - * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread + * @param wait_object Kernel object that we are waiting on, defaults to current thread + */ +void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread()); + +/** + * Schedules an event to wake up the specified thread after the specified delay. + * @param handle The thread handle. + * @param nanoseconds The time this thread will be allowed to sleep for. */ -void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); +void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); /** * Puts the current thread in the wait state for the given type * @param wait_type Type of wait - * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread + * @param wait_object Kernel object that we are waiting on * @param wait_address Arbitration address used to resume from wait */ -void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); +void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address); -/// Put current thread in a wait state - on WaitSynchronization -void WaitThread_Synchronization(); -/// Get the priority of the thread specified by handle -ResultVal<u32> GetThreadPriority(const Handle handle); - -/// Set the priority of the thread specified by handle -ResultCode SetThreadPriority(Handle handle, s32 priority); /** * Sets up the idle thread, this is a thread that is intended to never execute instructions, @@ -111,10 +156,6 @@ ResultCode SetThreadPriority(Handle handle, s32 priority); * @returns The handle of the idle thread */ Handle SetupIdleThread(); - -/// Whether the current thread is an idle thread -bool IsIdleThread(Handle thread); - /// Initialize threading void ThreadingInit(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp new file mode 100644 index 000000000..3b0452d4d --- /dev/null +++ b/src/core/hle/kernel/timer.cpp @@ -0,0 +1,144 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <set> + +#include "common/common.h" + +#include "core/core_timing.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/timer.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +class Timer : public Object { +public: + std::string GetTypeName() const override { return "Timer"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Timer; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + ResetType reset_type; ///< The ResetType of this timer + + bool signaled; ///< Whether the timer has been signaled or not + std::set<Handle> waiting_threads; ///< Threads that are waiting for the timer + std::string name; ///< Name of timer (optional) + + u64 initial_delay; ///< The delay until the timer fires for the first time + u64 interval_delay; ///< The delay until the timer fires after the first time + + ResultVal<bool> WaitSynchronization() override { + bool wait = !signaled; + if (wait) { + waiting_threads.insert(GetCurrentThread()->GetHandle()); + Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); + } + return MakeResult<bool>(wait); + } +}; + +/** + * Creates a timer. + * @param handle Reference to handle for the newly created timer + * @param reset_type ResetType describing how to create timer + * @param name Optional name of timer + * @return Newly created Timer object + */ +Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) { + Timer* timer = new Timer; + + handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE); + + timer->reset_type = reset_type; + timer->signaled = false; + timer->name = name; + timer->initial_delay = 0; + timer->interval_delay = 0; + return timer; +} + +ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) { + CreateTimer(*handle, reset_type, name); + return RESULT_SUCCESS; +} + +ResultCode ClearTimer(Handle handle) { + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); + + if (timer == nullptr) + return InvalidHandle(ErrorModule::Kernel); + + timer->signaled = false; + return RESULT_SUCCESS; +} + +/// The event type of the generic timer callback event +static int TimerCallbackEventType = -1; + +/// The timer callback event, called when a timer is fired +static void TimerCallback(u64 timer_handle, int cycles_late) { + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(timer_handle); + + if (timer == nullptr) { + LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle); + return; + } + + LOG_TRACE(Kernel, "Timer %u fired", timer_handle); + + timer->signaled = true; + + // Resume all waiting threads + for (Handle thread_handle : timer->waiting_threads) { + if (SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(thread_handle)) + thread->ResumeFromWait(); + } + + timer->waiting_threads.clear(); + + if (timer->reset_type == RESETTYPE_ONESHOT) + timer->signaled = false; + + if (timer->interval_delay != 0) { + // Reschedule the timer with the interval delay + u64 interval_microseconds = timer->interval_delay / 1000; + CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, + TimerCallbackEventType, timer_handle); + } +} + +ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); + + if (timer == nullptr) + return InvalidHandle(ErrorModule::Kernel); + + timer->initial_delay = initial; + timer->interval_delay = interval; + + u64 initial_microseconds = initial / 1000; + CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle); + return RESULT_SUCCESS; +} + +ResultCode CancelTimer(Handle handle) { + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); + + if (timer == nullptr) + return InvalidHandle(ErrorModule::Kernel); + + CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle); + return RESULT_SUCCESS; +} + +void TimersInit() { + TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); +} + +void TimersShutdown() { +} + +} // namespace diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h new file mode 100644 index 000000000..f8aa66b60 --- /dev/null +++ b/src/core/hle/kernel/timer.h @@ -0,0 +1,47 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/svc.h" + +namespace Kernel { + +/** + * Cancels a timer + * @param handle Handle of the timer to cancel + */ +ResultCode CancelTimer(Handle handle); + +/** + * Starts a timer with the specified initial delay and interval + * @param handle Handle of the timer to start + * @param initial Delay until the timer is first fired + * @param interval Delay until the timer is fired after the first time + */ +ResultCode SetTimer(Handle handle, s64 initial, s64 interval); + +/** + * Clears a timer + * @param handle Handle of the timer to clear + */ +ResultCode ClearTimer(Handle handle); + +/** + * Creates a timer + * @param Handle to newly created Timer object + * @param reset_type ResetType describing how to create the timer + * @param name Optional name of timer + * @return ResultCode of the error + */ +ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown"); + +/// Initializes the required variables for timers +void TimersInit(); +/// Tears down the timer variables +void TimersShutdown(); +} // namespace diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 0e9c213e0..82dcf5bba 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -369,14 +369,14 @@ private: StorageType storage; ResultCode result_code; -#if _DEBUG +#ifdef _DEBUG // The purpose of this pointer is to aid inspecting the type with a debugger, eliminating the // need to cast `storage` to a pointer or pay attention to `result_code`. const T* debug_ptr; #endif void UpdateDebugPtr() { -#if _DEBUG +#ifdef _DEBUG debug_ptr = empty() ? nullptr : static_cast<const T*>(static_cast<const void*>(&storage)); #endif } diff --git a/src/core/hle/service/apt_s.cpp b/src/core/hle/service/apt_s.cpp new file mode 100644 index 000000000..f4599e19d --- /dev/null +++ b/src/core/hle/service/apt_s.cpp @@ -0,0 +1,121 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + + +#include "common/common.h" +#include "common/file_util.h" + +#include "core/hle/hle.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/apt_s.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace APT_S + +namespace APT_U { + extern void GetLockHandle(Service::Interface* self); + extern void Initialize(Service::Interface* self); + extern void Enable(Service::Interface* self); + extern void InquireNotification(Service::Interface* self); + extern void GetSharedFont(Service::Interface* self); + extern void AppletUtility(Service::Interface* self); + extern void GlanceParameter(Service::Interface* self); + extern void ReceiveParameter(Service::Interface* self); +} + +namespace APT_S { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010040, APT_U::GetLockHandle, "GetLockHandle"}, + {0x00020080, APT_U::Initialize, "Initialize"}, + {0x00030040, APT_U::Enable, "Enable"}, + {0x00040040, nullptr, "Finalize"}, + {0x00050040, nullptr, "GetAppletManInfo"}, + {0x00060040, nullptr, "GetAppletInfo"}, + {0x00070000, nullptr, "GetLastSignaledAppletId"}, + {0x00080000, nullptr, "CountRegisteredApplet"}, + {0x00090040, nullptr, "IsRegistered"}, + {0x000A0040, nullptr, "GetAttribute"}, + {0x000B0040, APT_U::InquireNotification, "InquireNotification"}, + {0x000C0104, nullptr, "SendParameter"}, + {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter"}, + {0x000E0080, APT_U::GlanceParameter, "GlanceParameter"}, + {0x000F0100, nullptr, "CancelParameter"}, + {0x001000C2, nullptr, "DebugFunc"}, + {0x001100C0, nullptr, "MapProgramIdForDebug"}, + {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, + {0x00130000, nullptr, "GetPreparationState"}, + {0x00140040, nullptr, "SetPreparationState"}, + {0x00150140, nullptr, "PrepareToStartApplication"}, + {0x00160040, nullptr, "PreloadLibraryApplet"}, + {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, + {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, + {0x00190040, nullptr, "PrepareToStartSystemApplet"}, + {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, + {0x001B00C4, nullptr, "StartApplication"}, + {0x001C0000, nullptr, "WakeupApplication"}, + {0x001D0000, nullptr, "CancelApplication"}, + {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001F0084, nullptr, "StartSystemApplet"}, + {0x00200044, nullptr, "StartNewestHomeMenu"}, + {0x00210000, nullptr, "OrderToCloseApplication"}, + {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00230040, nullptr, "PrepareToJumpToApplication"}, + {0x00240044, nullptr, "JumpToApplication"}, + {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, + {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, + {0x00270044, nullptr, "CloseApplication"}, + {0x00280044, nullptr, "CloseLibraryApplet"}, + {0x00290044, nullptr, "CloseSystemApplet"}, + {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, + {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, + {0x002C0044, nullptr, "JumpToHomeMenu"}, + {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, + {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, + {0x00300044, nullptr, "LeaveResidentApplet"}, + {0x00310100, nullptr, "PrepareToDoApplicationJump"}, + {0x00320084, nullptr, "DoApplicationJump"}, + {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00340084, nullptr, "SendDeliverArg"}, + {0x00350080, nullptr, "ReceiveDeliverArg"}, + {0x00360040, nullptr, "LoadSysMenuArg"}, + {0x00370042, nullptr, "StoreSysMenuArg"}, + {0x00380040, nullptr, "PreloadResidentApplet"}, + {0x00390040, nullptr, "PrepareToStartResidentApplet"}, + {0x003A0044, nullptr, "StartResidentApplet"}, + {0x003B0040, nullptr, "CancelLibraryApplet"}, + {0x003C0042, nullptr, "SendDspSleep"}, + {0x003D0042, nullptr, "SendDspWakeUp"}, + {0x003E0080, nullptr, "ReplySleepQuery"}, + {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, + {0x00400042, nullptr, "SendCaptureBufferInfo"}, + {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, + {0x00420080, nullptr, "SleepSystem"}, + {0x00430040, nullptr, "NotifyToWait"}, + {0x00440000, APT_U::GetSharedFont, "GetSharedFont"}, + {0x00450040, nullptr, "GetWirelessRebootInfo"}, + {0x00460104, nullptr, "Wrap"}, + {0x00470104, nullptr, "Unwrap"}, + {0x00480100, nullptr, "GetProgramInfo"}, + {0x00490180, nullptr, "Reboot"}, + {0x004A0040, nullptr, "GetCaptureInfo"}, + {0x004B00C2, APT_U::AppletUtility, "AppletUtility"}, + {0x004C0000, nullptr, "SetFatalErrDispMode"}, + {0x004D0080, nullptr, "GetAppletProgramInfo"}, + {0x004E0000, nullptr, "HardwareResetAsync"}, + {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, + {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable, ARRAY_SIZE(FunctionTable)); +} + +} // namespace diff --git a/src/core/hle/service/apt_s.h b/src/core/hle/service/apt_s.h new file mode 100644 index 000000000..f097c9747 --- /dev/null +++ b/src/core/hle/service/apt_s.h @@ -0,0 +1,30 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace APT_S + +namespace APT_S { + +// Application and title launching service. These services handle signaling for home/power button as +// well. Only one session for either APT service can be open at a time, normally processes close the +// service handle immediately once finished using the service. The commands for APT:U and APT:S are +// exactly the same, however certain commands are only accessible with APT:S(NS module will call +// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services. + +/// Interface to "APT:S" service +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "APT:S"; + } +}; + +} // namespace diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp index d8b261ba7..69a7bcf92 100644 --- a/src/core/hle/service/apt_u.cpp +++ b/src/core/hle/service/apt_u.cpp @@ -10,7 +10,7 @@ #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" -#include "apt_u.h" +#include "core/hle/service/apt_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace APT_U @@ -25,10 +25,12 @@ namespace APT_U { // correctly mapping it in Citra, however we still do not understand how the mapping is determined. static const VAddr SHARED_FONT_VADDR = 0x18000000; -// Handle to shared memory region designated to for shared system font +/// Handle to shared memory region designated to for shared system font static Handle shared_font_mem = 0; static Handle lock_handle = 0; +static Handle notification_event_handle = 0; ///< APT notification event handle +static Handle pause_event_handle = 0; ///< APT pause event handle static std::vector<u8> shared_font; /// Signals used by APT functions @@ -42,18 +44,28 @@ enum class SignalType : u32 { void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle - cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle + notification_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Notification"); + pause_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); - Kernel::SetEventLocked(cmd_buff[3], true); - Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event + cmd_buff[3] = notification_event_handle; + cmd_buff[4] = pause_event_handle; + + Kernel::SetEventLocked(notification_event_handle, true); + Kernel::SetEventLocked(pause_event_handle, false); // Fire start event _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock"); Kernel::ReleaseMutex(lock_handle); cmd_buff[1] = 0; // No error +} - LOG_DEBUG(Service_APT, "called"); +void NotifyToWait(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 app_id = cmd_buff[1]; + // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further. + Kernel::SignalEvent(pause_event_handle); + LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); + cmd_buff[1] = 0; } void GetLockHandle(Service::Interface* self) { @@ -86,7 +98,7 @@ void Enable(Service::Interface* self) { void InquireNotification(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[2]; + u32 app_id = cmd_buff[1]; cmd_buff[1] = 0; // No error cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); @@ -194,8 +206,6 @@ void AppletUtility(Service::Interface* self) { * 4 : Handle to shared font memory */ void GetSharedFont(Service::Interface* self) { - LOG_TRACE(Kernel_SVC, "called"); - u32* cmd_buff = Kernel::GetCommandBuffer(); if (!shared_font.empty()) { @@ -281,7 +291,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00400042, nullptr, "SendCaptureBufferInfo"}, {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, {0x00420080, nullptr, "SleepSystem"}, - {0x00430040, nullptr, "NotifyToWait"}, + {0x00430040, NotifyToWait, "NotifyToWait"}, {0x00440000, GetSharedFont, "GetSharedFont"}, {0x00450040, nullptr, "GetWirelessRebootInfo"}, {0x00460104, nullptr, "Wrap"}, diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp new file mode 100644 index 000000000..cf4e82152 --- /dev/null +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -0,0 +1,98 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/cfg/cfg_s.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CFG_S + +namespace CFG_S { + +/** + * CFG_S::GetConfigInfoBlk2 service function + * Inputs: + * 0 : 0x00010082 + * 1 : Size + * 2 : Block ID + * 3 : Descriptor for the output buffer + * 4 : Output buffer pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void GetConfigInfoBlk2(Service::Interface* self) { + u32* cmd_buffer = Kernel::GetCommandBuffer(); + u32 size = cmd_buffer[1]; + u32 block_id = cmd_buffer[2]; + u8* data_pointer = Memory::GetPointer(cmd_buffer[4]); + + if (data_pointer == nullptr) { + cmd_buffer[1] = -1; // TODO(Subv): Find the right error code + return; + } + + cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw; +} + +/** + * CFG_S::GetConfigInfoBlk8 service function + * Inputs: + * 0 : 0x04010082 + * 1 : Size + * 2 : Block ID + * 3 : Descriptor for the output buffer + * 4 : Output buffer pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void GetConfigInfoBlk8(Service::Interface* self) { + u32* cmd_buffer = Kernel::GetCommandBuffer(); + u32 size = cmd_buffer[1]; + u32 block_id = cmd_buffer[2]; + u8* data_pointer = Memory::GetPointer(cmd_buffer[4]); + + if (data_pointer == nullptr) { + cmd_buffer[1] = -1; // TODO(Subv): Find the right error code + return; + } + + cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw; +} + +/** + * CFG_S::UpdateConfigNANDSavegame service function + * Inputs: + * 0 : 0x04030000 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UpdateConfigNANDSavegame(Service::Interface* self) { + u32* cmd_buffer = Kernel::GetCommandBuffer(); + cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw; +} + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, + {0x00020000, nullptr, "SecureInfoGetRegion"}, + {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, + {0x04020082, nullptr, "SetConfigInfoBlk4"}, + {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, + {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, + {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, + {0x04060000, nullptr, "SecureInfoGetRegion"}, + {0x04070000, nullptr, "SecureInfoGetByte101"}, + {0x04080042, nullptr, "SecureInfoGetSerialNo"}, + {0x04090000, nullptr, "UpdateConfigBlk00040003"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable, ARRAY_SIZE(FunctionTable)); +} + +} // namespace diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h new file mode 100644 index 000000000..d8b67137f --- /dev/null +++ b/src/core/hle/service/cfg/cfg_s.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CFG_S + +namespace CFG_S { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "cfg:s"; + } +}; + +} // namespace diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index d4affdfbf..d5e39ea4b 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -23,11 +23,8 @@ void SignalInterrupt() { // that check the DSP interrupt signal event to run. We should figure out the different types of // DSP interrupts, and trigger them at the appropriate times. - if (interrupt_event == 0) { - LOG_WARNING(Service_DSP, "cannot signal interrupt until DSP event has been created!"); - return; - } - Kernel::SignalEvent(interrupt_event); + if (interrupt_event != 0) + Kernel::SignalEvent(interrupt_event); } /** diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 7eb32146d..56f3117f4 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -27,8 +27,6 @@ static void Initialize(Service::Interface* self) { // TODO(Link Mauve): check the behavior when cmd_buff[1] isn't 32, as per // http://3dbrew.org/wiki/FS:Initialize#Request cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_DEBUG(Service_FS, "called"); } /** @@ -104,8 +102,8 @@ static void OpenFileDirectly(Service::Interface* self) { FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); FileSys::Path file_path(filename_type, filename_size, filename_ptr); - LOG_DEBUG(Service_FS, "archive_path=%s file_path=%s, mode=%u attributes=%d", - archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes); + LOG_DEBUG(Service_FS, "archive_id=0x%08X archive_path=%s file_path=%s, mode=%u attributes=%d", + archive_id, archive_path.DebugStr().c_str(), file_path.DebugStr().c_str(), mode.hex, attributes); ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); if (archive_handle.Failed()) { @@ -367,7 +365,7 @@ static void OpenArchive(Service::Interface* self) { u32 archivename_ptr = cmd_buff[5]; FileSys::Path archive_path(archivename_type, archivename_size, archivename_ptr); - LOG_DEBUG(Service_FS, "archive_path=%s", archive_path.DebugStr().c_str()); + LOG_DEBUG(Service_FS, "archive_id=0x%08X archive_path=%s", archive_id, archive_path.DebugStr().c_str()); ResultVal<ArchiveHandle> handle = OpenArchive(archive_id, archive_path); cmd_buff[1] = handle.Code().raw; @@ -408,8 +406,6 @@ static void IsSdmcDetected(Service::Interface* self) { cmd_buff[1] = 0; cmd_buff[2] = Settings::values.use_virtual_sd ? 1 : 0; - - LOG_DEBUG(Service_FS, "called"); } /** diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 26a43217e..4ca2b9bd0 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -210,14 +210,27 @@ void SignalInterrupt(InterruptId interrupt_id) { } for (int thread_id = 0; thread_id < 0x4; ++thread_id) { InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(thread_id); - interrupt_relay_queue->number_interrupts = interrupt_relay_queue->number_interrupts + 1; - u8 next = interrupt_relay_queue->index; next += interrupt_relay_queue->number_interrupts; next = next % 0x34; // 0x34 is the number of interrupt slots + interrupt_relay_queue->number_interrupts += 1; + interrupt_relay_queue->slot[next] = interrupt_id; interrupt_relay_queue->error_code = 0x0; // No error + + // Update framebuffer information if requested + // TODO(yuriks): Confirm where this code should be called. It is definitely updated without + // executing any GSP commands, only waiting on the event. + for (int screen_id = 0; screen_id < 2; ++screen_id) { + FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); + + if (info->is_dirty) { + SetBufferSwap(screen_id, info->framebuffer_info[info->index]); + } + + info->is_dirty = false; + } } Kernel::SignalEvent(g_interrupt_event); } @@ -269,8 +282,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); - - SignalInterrupt(InterruptId::PSC0); break; } @@ -283,22 +294,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size); WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); - - // TODO(bunnei): Determine if these interrupts should be signalled here. - SignalInterrupt(InterruptId::PSC1); - SignalInterrupt(InterruptId::PPF); - - // Update framebuffer information if requested - for (int screen_id = 0; screen_id < 2; ++screen_id) { - FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); - - if (info->is_dirty) { - SetBufferSwap(screen_id, info->framebuffer_info[info->index]); - info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1; - } - - info->is_dirty = false; - } break; } @@ -331,9 +326,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { /// This triggers handling of the GX command written to the command buffer in shared memory. static void TriggerCmdReqQueue(Service::Interface* self) { - - LOG_TRACE(Service_GSP, "called"); - // Iterate through each thread's command queue... for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 932b6170f..65abb194a 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -45,21 +45,16 @@ enum class CommandId : u32 { /// GSP thread interrupt relay queue struct InterruptRelayQueue { - union { - u32 hex; - - // Index of last interrupt in the queue - BitField<0,8,u32> index; - - // Number of interrupts remaining to be processed by the userland code - BitField<8,8,u32> number_interrupts; - - // Error code - zero on success, otherwise an error has occurred - BitField<16,8,u32> error_code; - }; - - u32 unk0; - u32 unk1; + // Index of last interrupt in the queue + u8 index; + // Number of interrupts remaining to be processed by the userland code + u8 number_interrupts; + // Error code - zero on success, otherwise an error has occurred + u8 error_code; + u8 padding1; + + u32 missed_PDC0; + u32 missed_PDC1; InterruptId slot[0x34]; ///< Interrupt ID slots }; diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp index 99b0ea5a0..1403b1de9 100644 --- a/src/core/hle/service/hid_user.cpp +++ b/src/core/hle/service/hid_user.cpp @@ -4,6 +4,7 @@ #include "common/log.h" +#include "core/arm/arm_interface.h" #include "core/hle/hle.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" @@ -162,8 +163,6 @@ static void GetIPCHandles(Service::Interface* self) { cmd_buff[6] = event_accelerometer; cmd_buff[7] = event_gyroscope; cmd_buff[8] = event_debug_pad; - - LOG_TRACE(Service_HID, "called"); } const Interface::FunctionInfo FunctionTable[] = { diff --git a/src/core/hle/service/ptm_sysm.cpp b/src/core/hle/service/ptm_sysm.cpp new file mode 100644 index 000000000..4b5f86a47 --- /dev/null +++ b/src/core/hle/service/ptm_sysm.cpp @@ -0,0 +1,56 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "common/make_unique.h" +#include "core/file_sys/archive_extsavedata.h" +#include "core/hle/hle.h" +#include "core/hle/service/ptm_sysm.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace PTM_SYSM + +namespace PTM_SYSM { + +const Interface::FunctionInfo FunctionTable[] = { + {0x040100C0, nullptr, "SetRtcAlarmEx"}, + {0x04020042, nullptr, "ReplySleepQuery"}, + {0x04030042, nullptr, "NotifySleepPreparationComplete"}, + {0x04040102, nullptr, "SetWakeupTrigger"}, + {0x04050000, nullptr, "GetAwakeReason"}, + {0x04060000, nullptr, "RequestSleep"}, + {0x040700C0, nullptr, "ShutdownAsync"}, + {0x04080000, nullptr, "Awake"}, + {0x04090080, nullptr, "RebootAsync"}, + {0x040A0000, nullptr, "CheckNew3DS"}, + {0x08010640, nullptr, "SetInfoLEDPattern"}, + {0x08020040, nullptr, "SetInfoLEDPatternHeader"}, + {0x08030000, nullptr, "GetInfoLEDStatus"}, + {0x08040040, nullptr, "SetBatteryEmptyLEDPattern"}, + {0x08050000, nullptr, "ClearStepHistory"}, + {0x080600C2, nullptr, "SetStepHistory"}, + {0x08070082, nullptr, "GetPlayHistory"}, + {0x08080000, nullptr, "GetPlayHistoryStart"}, + {0x08090000, nullptr, "GetPlayHistoryLength"}, + {0x080A0000, nullptr, "ClearPlayHistory"}, + {0x080B0080, nullptr, "CalcPlayHistoryStart"}, + {0x080C0080, nullptr, "SetUserTime"}, + {0x080D0000, nullptr, "InvalidateSystemTime"}, + {0x080E0140, nullptr, "NotifyPlayEvent"}, + {0x080F0000, nullptr, "IsLegacyPowerOff"}, + {0x08100000, nullptr, "ClearLegacyPowerOff"}, + {0x08110000, nullptr, "GetShellStatus"}, + {0x08120000, nullptr, "IsShutdownByBatteryEmpty"}, + {0x08130000, nullptr, "FormatSavedata"}, + {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"} +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable, ARRAY_SIZE(FunctionTable)); +} + +} // namespace diff --git a/src/core/hle/service/ptm_sysm.h b/src/core/hle/service/ptm_sysm.h new file mode 100644 index 000000000..0f267b214 --- /dev/null +++ b/src/core/hle/service/ptm_sysm.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace PTM_SYSM + +namespace PTM_SYSM { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "ptm:sysm"; + } +}; + +} // namespace diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp index fd79cd8ab..753180add 100644 --- a/src/core/hle/service/ptm_u.cpp +++ b/src/core/hle/service/ptm_u.cpp @@ -76,8 +76,6 @@ static void GetShellState(Service::Interface* self) { cmd_buff[1] = 0; cmd_buff[2] = shell_open ? 1 : 0; - - LOG_TRACE(Service_PTM, "PTM_U::GetShellState called"); } /** diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 0c5597283..446ed5164 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -11,10 +11,12 @@ #include "core/hle/service/am_app.h" #include "core/hle/service/am_net.h" #include "core/hle/service/apt_a.h" +#include "core/hle/service/apt_s.h" #include "core/hle/service/apt_u.h" #include "core/hle/service/boss_u.h" #include "core/hle/service/cecd_u.h" #include "core/hle/service/cfg/cfg_i.h" +#include "core/hle/service/cfg/cfg_s.h" #include "core/hle/service/cfg/cfg_u.h" #include "core/hle/service/csnd_snd.h" #include "core/hle/service/dsp_dsp.h" @@ -34,6 +36,7 @@ #include "core/hle/service/nwm_uds.h" #include "core/hle/service/pm_app.h" #include "core/hle/service/ptm_u.h" +#include "core/hle/service/ptm_sysm.h" #include "core/hle/service/soc_u.h" #include "core/hle/service/srv.h" #include "core/hle/service/ssl_c.h" @@ -59,7 +62,8 @@ void Manager::DeleteService(const std::string& port_name) { } Interface* Manager::FetchFromHandle(Handle handle) { - return Kernel::g_handle_table.Get<Interface>(handle); + // TODO(yuriks): This function is very suspicious and should probably be exterminated. + return Kernel::g_handle_table.Get<Interface>(handle).get(); } Interface* Manager::FetchFromPortName(const std::string& port_name) { @@ -84,10 +88,12 @@ void Init() { g_manager->AddService(new AM_APP::Interface); g_manager->AddService(new AM_NET::Interface); g_manager->AddService(new APT_A::Interface); + g_manager->AddService(new APT_S::Interface); g_manager->AddService(new APT_U::Interface); g_manager->AddService(new BOSS_U::Interface); g_manager->AddService(new CECD_U::Interface); g_manager->AddService(new CFG_I::Interface); + g_manager->AddService(new CFG_S::Interface); g_manager->AddService(new CFG_U::Interface); g_manager->AddService(new CSND_SND::Interface); g_manager->AddService(new DSP_DSP::Interface); @@ -107,6 +113,7 @@ void Init() { g_manager->AddService(new NWM_UDS::Interface); g_manager->AddService(new PM_APP::Interface); g_manager->AddService(new PTM_U::Interface); + g_manager->AddService(new PTM_SYSM::Interface); g_manager->AddService(new SOC_U::Interface); g_manager->AddService(new SSL_C::Interface); g_manager->AddService(new Y2R_U::Interface); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 41ba1e554..e75d5008b 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -33,6 +33,22 @@ class Interface : public Kernel::Session { // processes. friend class Manager; + + /** + * Creates a function string for logging, complete with the name (or header code, depending + * on what's passed in) the port name, and all the cmd_buff arguments. + */ + std::string MakeFunctionString(const std::string& name, const std::string& port_name, const u32* cmd_buff) { + // Number of params == bits 0-5 + bits 6-11 + int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); + + std::string function_string = Common::StringFromFormat("function '%s': port=%s", name.c_str(), port_name.c_str()); + for (int i = 1; i <= num_params; ++i) { + function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); + } + return function_string; + } + public: std::string GetName() const override { return GetPortName(); } @@ -72,21 +88,14 @@ public: auto itr = m_functions.find(cmd_buff[0]); if (itr == m_functions.end() || itr->second.func == nullptr) { - // Number of params == bits 0-5 + bits 6-11 - int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); - - std::string error = "unknown/unimplemented function '%s': port=%s"; - for (int i = 1; i <= num_params; ++i) { - error += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); - } - - std::string name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; - - LOG_ERROR(Service, error.c_str(), name.c_str(), GetPortName().c_str()); + std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; + LOG_ERROR(Service, "%s %s", "unknown/unimplemented", MakeFunctionString(function_name, GetPortName(), cmd_buff).c_str()); // TODO(bunnei): Hack - ignore error cmd_buff[1] = 0; return MakeResult<bool>(false); + } else { + LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName(), cmd_buff).c_str()); } itr->second.func(this); diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index f502c6afe..bb8ee86be 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -7,6 +7,19 @@ #if EMU_PLATFORM == PLATFORM_WINDOWS #include <winsock2.h> #include <ws2tcpip.h> + +// MinGW does not define several errno constants +#ifndef _MSC_VER +#define EBADMSG 104 +#define ENODATA 120 +#define ENOMSG 122 +#define ENOSR 124 +#define ENOSTR 125 +#define ETIME 137 +#define EIDRM 2001 +#define ENOLINK 2002 +#endif // _MSC_VER + #else #include <sys/socket.h> #include <netinet/in.h> diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 912b52adf..ac5f30a28 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -14,16 +14,12 @@ namespace SRV { static Handle g_event_handle = 0; static void Initialize(Service::Interface* self) { - LOG_DEBUG(Service_SRV, "called"); - u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = 0; // No error } static void GetProcSemaphore(Service::Interface* self) { - LOG_TRACE(Service_SRV, "called"); - u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(bunnei): Change to a semaphore once these have been implemented diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index f9e3619dd..b3d873ef0 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -12,6 +12,21 @@ namespace Y2R_U { +/** + * Y2R_U::IsBusyConversion service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Whether the current conversion is of type busy conversion (?) + */ +static void IsBusyConversion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw;; + cmd_buff[2] = 0; + + LOG_WARNING(Service, "(STUBBED) called"); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010040, nullptr, "SetInputFormat"}, {0x00030040, nullptr, "SetOutputFormat"}, @@ -29,7 +44,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00220040, nullptr, "SetAlpha"}, {0x00260000, nullptr, "StartConversion"}, {0x00270000, nullptr, "StopConversion"}, - {0x00280000, nullptr, "IsBusyConversion"}, + {0x00280000, IsBusyConversion, "IsBusyConversion"}, {0x002A0000, nullptr, "PingProcess"}, {0x002B0000, nullptr, "DriverInitialize"}, {0x002C0000, nullptr, "DriverFinalize"} diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index c25409a9f..a487f757c 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -7,6 +7,7 @@ #include "common/string_util.h" #include "common/symbols.h" +#include "core/arm/arm_interface.h" #include "core/mem_map.h" #include "core/hle/kernel/address_arbiter.h" @@ -15,6 +16,7 @@ #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/timer.h" #include "core/hle/function_wrappers.h" #include "core/hle/result.h" @@ -23,6 +25,8 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace SVC +using Kernel::SharedPtr; + namespace SVC { enum ControlMemoryOperation { @@ -92,7 +96,7 @@ static Result ConnectToPort(Handle* out, const char* port_name) { /// Synchronize to an OS service static Result SendSyncRequest(Handle handle) { - Kernel::Session* session = Kernel::g_handle_table.Get<Kernel::Session>(handle); + SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle); if (session == nullptr) { return InvalidHandle(ErrorModule::Kernel).raw; } @@ -116,20 +120,19 @@ static Result CloseHandle(Handle handle) { /// Wait for a handle to synchronize, timeout after the specified nanoseconds static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { - // TODO(bunnei): Do something with nano_seconds, currently ignoring this - bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated - - Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handle); + SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handle); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; - LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), - object->GetName().c_str(), nano_seconds); + LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, + object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); ResultVal<bool> wait = object->WaitSynchronization(); // Check for next thread to schedule if (wait.Succeeded() && *wait) { + // Create an event to wake the thread up after the specified nanosecond delay has passed + Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nano_seconds); HLE::Reschedule(__func__); } @@ -139,6 +142,7 @@ static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { /// Wait for the given handles to synchronize, timeout after the specified nanoseconds static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { + // TODO(bunnei): Do something with nano_seconds, currently ignoring this bool unlock_all = true; bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated @@ -148,12 +152,12 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, // Iterate through each handle, synchronize kernel object for (s32 i = 0; i < handle_count; i++) { - Kernel::Object* object = Kernel::g_handle_table.GetGeneric(handles[i]); + SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]); if (object == nullptr) return InvalidHandle(ErrorModule::Kernel).raw; - LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName().c_str(), - object->GetName().c_str()); + LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], + object->GetTypeName().c_str(), object->GetName().c_str()); // TODO(yuriks): Verify how the real function behaves when an error happens here ResultVal<bool> wait_result = object->WaitSynchronization(); @@ -180,7 +184,6 @@ static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, /// Create an address arbiter (to allocate access to shared resources) static Result CreateAddressArbiter(u32* arbiter) { - LOG_TRACE(Kernel_SVC, "called"); Handle handle = Kernel::CreateAddressArbiter(); *arbiter = handle; return 0; @@ -191,7 +194,7 @@ static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter, address, type, value); return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), - address, value).raw; + address, value, nanoseconds).raw; } /// Used to output a message on a debug hardware unit - does nothing on a retail unit @@ -220,6 +223,8 @@ static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, /// Creates a new thread static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { + using Kernel::Thread; + std::string name; if (Symbols::HasSymbol(entry_point)) { TSymbol symbol = Symbols::GetSymbol(entry_point); @@ -228,41 +233,53 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top name = Common::StringFromFormat("unknown-%08x", entry_point); } - Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id, - stack_top); + ResultVal<SharedPtr<Thread>> thread_res = Kernel::Thread::Create( + name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE); + if (thread_res.Failed()) + return thread_res.Code().raw; + SharedPtr<Thread> thread = std::move(*thread_res); - Core::g_app_core->SetReg(1, thread); + // TODO(yuriks): Create new handle instead of using built-in + Core::g_app_core->SetReg(1, thread->GetHandle()); LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, - name.c_str(), arg, stack_top, priority, processor_id, thread); + name.c_str(), arg, stack_top, priority, processor_id, thread->GetHandle()); + + if (THREADPROCESSORID_1 == processor_id) { + LOG_WARNING(Kernel_SVC, + "thread designated for system CPU core (UNIMPLEMENTED) will be run with app core scheduling"); + } return 0; } /// Called when a thread exits -static u32 ExitThread() { - Handle thread = Kernel::GetCurrentThreadHandle(); - - LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C +static void ExitThread() { + LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); - Kernel::StopThread(thread, __func__); + Kernel::GetCurrentThread()->Stop(__func__); HLE::Reschedule(__func__); - return 0; } /// Gets the priority for the specified thread static Result GetThreadPriority(s32* priority, Handle handle) { - ResultVal<u32> priority_result = Kernel::GetThreadPriority(handle); - if (priority_result.Succeeded()) { - *priority = *priority_result; - } - return priority_result.Code().raw; + const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); + if (thread == nullptr) + return InvalidHandle(ErrorModule::Kernel).raw; + + *priority = thread->GetPriority(); + return RESULT_SUCCESS.raw; } /// Sets the priority for the specified thread static Result SetThreadPriority(Handle handle, s32 priority) { - return Kernel::SetThreadPriority(handle, priority).raw; + SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); + if (thread == nullptr) + return InvalidHandle(ErrorModule::Kernel).raw; + + thread->SetPriority(priority); + return RESULT_SUCCESS.raw; } /// Create a mutex @@ -283,8 +300,13 @@ static Result ReleaseMutex(Handle handle) { /// Get the ID for the specified thread. static Result GetThreadId(u32* thread_id, Handle handle) { LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); - ResultCode result = Kernel::GetThreadId(thread_id, handle); - return result.raw; + + const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); + if (thread == nullptr) + return InvalidHandle(ErrorModule::Kernel).raw; + + *thread_id = thread->GetThreadId(); + return RESULT_SUCCESS.raw; } /// Creates a semaphore @@ -338,12 +360,42 @@ static Result ClearEvent(Handle evt) { return Kernel::ClearEvent(evt).raw; } +/// Creates a timer +static Result CreateTimer(Handle* handle, u32 reset_type) { + ResultCode res = Kernel::CreateTimer(handle, static_cast<ResetType>(reset_type)); + LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", + reset_type, *handle); + return res.raw; +} + +/// Clears a timer +static Result ClearTimer(Handle handle) { + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); + return Kernel::ClearTimer(handle).raw; +} + +/// Starts a timer +static Result SetTimer(Handle handle, s64 initial, s64 interval) { + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); + return Kernel::SetTimer(handle, initial, interval).raw; +} + +/// Cancels a timer +static Result CancelTimer(Handle handle) { + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); + return Kernel::CancelTimer(handle).raw; +} + /// Sleep the current thread static void SleepThread(s64 nanoseconds) { LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); // Sleep current thread and check for next thread to schedule Kernel::WaitCurrentThread(WAITTYPE_SLEEP); + + // Create an event to wake the thread up after the specified nanosecond delay has passed + Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); + HLE::Reschedule(__func__); } @@ -374,7 +426,7 @@ const HLE::FunctionDef SVC_Table[] = { {0x06, nullptr, "GetProcessIdealProcessor"}, {0x07, nullptr, "SetProcessIdealProcessor"}, {0x08, HLE::Wrap<CreateThread>, "CreateThread"}, - {0x09, HLE::Wrap<ExitThread>, "ExitThread"}, + {0x09, ExitThread, "ExitThread"}, {0x0A, HLE::Wrap<SleepThread>, "SleepThread"}, {0x0B, HLE::Wrap<GetThreadPriority>, "GetThreadPriority"}, {0x0C, HLE::Wrap<SetThreadPriority>, "SetThreadPriority"}, @@ -391,10 +443,10 @@ const HLE::FunctionDef SVC_Table[] = { {0x17, HLE::Wrap<CreateEvent>, "CreateEvent"}, {0x18, HLE::Wrap<SignalEvent>, "SignalEvent"}, {0x19, HLE::Wrap<ClearEvent>, "ClearEvent"}, - {0x1A, nullptr, "CreateTimer"}, - {0x1B, nullptr, "SetTimer"}, - {0x1C, nullptr, "CancelTimer"}, - {0x1D, nullptr, "ClearTimer"}, + {0x1A, HLE::Wrap<CreateTimer>, "CreateTimer"}, + {0x1B, HLE::Wrap<SetTimer>, "SetTimer"}, + {0x1C, HLE::Wrap<CancelTimer>, "CancelTimer"}, + {0x1D, HLE::Wrap<ClearTimer>, "ClearTimer"}, {0x1E, HLE::Wrap<CreateMemoryBlock>, "CreateMemoryBlock"}, {0x1F, HLE::Wrap<MapMemoryBlock>, "MapMemoryBlock"}, {0x20, nullptr, "UnmapMemoryBlock"}, diff --git a/src/core/hle/svc.h b/src/core/hle/svc.h index ad780818e..5d020a5ba 100644 --- a/src/core/hle/svc.h +++ b/src/core/hle/svc.h @@ -20,21 +20,6 @@ struct PageInfo { u32 flags; }; -struct ThreadContext { - u32 cpu_registers[13]; - u32 sp; - u32 lr; - u32 pc; - u32 cpsr; - u32 fpu_registers[32]; - u32 fpscr; - u32 fpexc; - - // These are not part of native ThreadContext, but needed by emu - u32 reg_15; - u32 mode; -}; - enum ResetType { RESETTYPE_ONESHOT, RESETTYPE_STICKY, |