aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/function_wrappers.h8
-rw-r--r--src/core/hle/hle.cpp1
-rw-r--r--src/core/hle/hle.h2
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp33
-rw-r--r--src/core/hle/kernel/address_arbiter.h2
-rw-r--r--src/core/hle/kernel/event.cpp16
-rw-r--r--src/core/hle/kernel/kernel.cpp32
-rw-r--r--src/core/hle/kernel/kernel.h26
-rw-r--r--src/core/hle/kernel/mutex.cpp23
-rw-r--r--src/core/hle/kernel/semaphore.cpp10
-rw-r--r--src/core/hle/kernel/shared_memory.cpp4
-rw-r--r--src/core/hle/kernel/thread.cpp438
-rw-r--r--src/core/hle/kernel/thread.h115
-rw-r--r--src/core/hle/kernel/timer.cpp144
-rw-r--r--src/core/hle/kernel/timer.h47
-rw-r--r--src/core/hle/result.h4
-rw-r--r--src/core/hle/service/apt_s.cpp121
-rw-r--r--src/core/hle/service/apt_s.h30
-rw-r--r--src/core/hle/service/apt_u.cpp32
-rw-r--r--src/core/hle/service/cfg/cfg_s.cpp98
-rw-r--r--src/core/hle/service/cfg/cfg_s.h23
-rw-r--r--src/core/hle/service/dsp_dsp.cpp7
-rw-r--r--src/core/hle/service/fs/fs_user.cpp10
-rw-r--r--src/core/hle/service/gsp_gpu.cpp38
-rw-r--r--src/core/hle/service/gsp_gpu.h25
-rw-r--r--src/core/hle/service/hid_user.cpp3
-rw-r--r--src/core/hle/service/ptm_sysm.cpp56
-rw-r--r--src/core/hle/service/ptm_sysm.h23
-rw-r--r--src/core/hle/service/ptm_u.cpp2
-rw-r--r--src/core/hle/service/service.cpp9
-rw-r--r--src/core/hle/service/service.h31
-rw-r--r--src/core/hle/service/soc_u.cpp13
-rw-r--r--src/core/hle/service/srv.cpp4
-rw-r--r--src/core/hle/service/y2r_u.cpp17
-rw-r--r--src/core/hle/svc.cpp122
-rw-r--r--src/core/hle/svc.h15
36 files changed, 1090 insertions, 494 deletions
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h
index 0f822f84..a2f51b41 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 f76048d1..11570c8b 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 59b770f0..3f6f9a4b 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 736bbc36..b7434aaf 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 030e7ad7..3ffd465a 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 e43c3ee4..271190db 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 ae2c11a1..d3684896 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 7f86fd07..5e5217b7 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 3dfeffc9..853a5dd7 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 6bc8066a..88ec9a10 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 cea1f6fa..5368e472 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 58fb62e8..bc86a7c5 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 dfe92d16..284dec40 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 00000000..3b0452d4
--- /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 00000000..f8aa66b6
--- /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 0e9c213e..82dcf5bb 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 00000000..f4599e19
--- /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 00000000..f097c974
--- /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 d8b261ba..69a7bcf9 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 00000000..cf4e8215
--- /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 00000000..d8b67137
--- /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 d4affdfb..d5e39ea4 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 7eb32146..56f3117f 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 26a43217..4ca2b9bd 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 932b6170..65abb194 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 99b0ea5a..1403b1de 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 00000000..4b5f86a4
--- /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 00000000..0f267b21
--- /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 fd79cd8a..753180ad 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 0c559728..446ed516 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 41ba1e55..e75d5008 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 f502c6af..bb8ee86b 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 912b52ad..ac5f30a2 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 f9e3619d..b3d873ef 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 c25409a9..a487f757 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 ad780818..5d020a5b 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,