From eec40f8f6f5fea63640721027408887291e19f61 Mon Sep 17 00:00:00 2001 From: archshift Date: Thu, 1 May 2014 15:13:06 -0700 Subject: [PATCH 01/72] Use runtime arguments to specify boot filename. --- src/citra/citra.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index d55b973936..00bab3203b 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -24,7 +24,7 @@ int __cdecl main(int argc, char **argv) { System::Init(emu_window); - std::string boot_filename = "homebrew.elf"; + std::string boot_filename = argv[1]; std::string error_str; bool res = Loader::LoadFile(boot_filename, &error_str); From 62528320ea31e94202d649855a5eef79e127edd9 Mon Sep 17 00:00:00 2001 From: archshift Date: Thu, 1 May 2014 20:41:42 -0700 Subject: [PATCH 02/72] Check arg count before attempting to access it. --- src/citra/citra.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 00bab3203b..d8325772e5 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -24,7 +24,14 @@ int __cdecl main(int argc, char **argv) { System::Init(emu_window); - std::string boot_filename = argv[1]; + std::string boot_filename; + + if (argc < 2) { + ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); + } + else { + boot_filename = argv[1]; + } std::string error_str; bool res = Loader::LoadFile(boot_filename, &error_str); From bd316ca0c0e244baf8f77b635eae85f744a62cf1 Mon Sep 17 00:00:00 2001 From: archshift Date: Sun, 4 May 2014 15:47:42 -0700 Subject: [PATCH 03/72] Fixed indents --- src/citra/citra.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index d8325772e5..80896505fd 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -25,13 +25,13 @@ int __cdecl main(int argc, char **argv) { System::Init(emu_window); std::string boot_filename; - - if (argc < 2) { - ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); - } - else { - boot_filename = argv[1]; - } + + if (argc < 2) { + ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); + } + else { + boot_filename = argv[1]; + } std::string error_str; bool res = Loader::LoadFile(boot_filename, &error_str); From daf7a7880a9e3b84c340aaf690310fb68e287372 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 May 2014 20:54:51 -0400 Subject: [PATCH 04/72] added kernel logger to common --- src/common/log.h | 4 +++- src/common/log_manager.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/common/log.h b/src/common/log.h index d95f51f564..8b39b03a17 100644 --- a/src/common/log.h +++ b/src/common/log.h @@ -5,6 +5,8 @@ #ifndef _LOG_H_ #define _LOG_H_ +#define LOGGING + #define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports. #define ERROR_LEVEL 2 // Critical errors #define WARNING_LEVEL 3 // Something is suspicious. @@ -53,7 +55,7 @@ enum LOG_TYPE { WII_IPC_ES, WII_IPC_FILEIO, WII_IPC_HID, - WII_IPC_HLE, + KERNEL, SVC, NDMA, HLE, diff --git a/src/common/log_manager.cpp b/src/common/log_manager.cpp index 80fd473b93..146472888d 100644 --- a/src/common/log_manager.cpp +++ b/src/common/log_manager.cpp @@ -60,13 +60,13 @@ LogManager::LogManager() m_Log[LogTypes::LOADER] = new LogContainer("Loader", "Loader"); m_Log[LogTypes::FILESYS] = new LogContainer("FileSys", "File System"); m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID"); - m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE"); + m_Log[LogTypes::KERNEL] = new LogContainer("KERNEL", "KERNEL HLE"); m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD"); m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO", "WII IPC FILEIO"); m_Log[LogTypes::RENDER] = new LogContainer("RENDER", "RENDER"); m_Log[LogTypes::LCD] = new LogContainer("LCD", "LCD"); - m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call"); + m_Log[LogTypes::SVC] = new LogContainer("SVC", "Supervisor Call HLE"); m_Log[LogTypes::NDMA] = new LogContainer("NDMA", "NDMA"); m_Log[LogTypes::HLE] = new LogContainer("HLE", "High Level Emulation"); m_Log[LogTypes::HW] = new LogContainer("HW", "Hardware"); From 6b264518a50ce21cb1be55ff3eac4e1c85582cfe Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 May 2014 22:11:18 -0400 Subject: [PATCH 05/72] added initial kernel/thread modules --- src/core/CMakeLists.txt | 2 + src/core/core.vcxproj | 4 + src/core/core.vcxproj.filters | 15 +++ src/core/hle/kernel/kernel.cpp | 142 ++++++++++++++++++++ src/core/hle/kernel/kernel.h | 121 +++++++++++++++++ src/core/hle/kernel/thread.cpp | 228 +++++++++++++++++++++++++++++++++ src/core/hle/kernel/thread.h | 36 ++++++ 7 files changed, 548 insertions(+) create mode 100644 src/core/hle/kernel/kernel.cpp create mode 100644 src/core/hle/kernel/kernel.h create mode 100644 src/core/hle/kernel/thread.cpp create mode 100644 src/core/hle/kernel/thread.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index eb4fef3815..3fa5f51f14 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -22,6 +22,8 @@ set(SRCS core.cpp hle/config_mem.cpp hle/coprocessor.cpp hle/syscall.cpp + hle/kernel/kernel.cpp + hle/kernel/thread.cpp hle/service/apt.cpp hle/service/gsp.cpp hle/service/hid.cpp diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index b56661e48b..59fc6f4fcf 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -156,6 +156,8 @@ + + @@ -198,6 +200,8 @@ + + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 5c947ec23f..ff988c1161 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -28,6 +28,9 @@ {812c5189-ca49-4704-b842-3ffad09092d3} + + {f2b132eb-caff-4b04-aaae-88d24393a711} + @@ -114,6 +117,12 @@ hle + + hle\kernel + + + hle\kernel + @@ -223,6 +232,12 @@ hle + + hle\kernel + + + hle\kernel + diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp new file mode 100644 index 0000000000..fc494fe30f --- /dev/null +++ b/src/core/hle/kernel/kernel.cpp @@ -0,0 +1,142 @@ +// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/common.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/thread.h" + +KernelObjectPool g_kernel_objects; + +void __KernelInit() { + __KernelThreadingInit(); +} + +void __KernelShutdown() { + __KernelThreadingShutdown(); +} + +KernelObjectPool::KernelObjectPool() { + memset(occupied, 0, sizeof(bool) * MAX_COUNT); + next_id = INITIAL_NEXT_ID; +} + +UID KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_top) { + if (range_top > MAX_COUNT) { + range_top = MAX_COUNT; + } + if (next_id >= range_bottom && next_id < range_top) { + range_bottom = next_id++; + } + for (int i = range_bottom; i < range_top; i++) { + if (!occupied[i]) { + occupied[i] = true; + pool[i] = obj; + pool[i]->uid = i + HANDLE_OFFSET; + return i + HANDLE_OFFSET; + } + } + ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); + return 0; +} + +bool KernelObjectPool::IsValid(UID handle) +{ + int index = handle - HANDLE_OFFSET; + if (index < 0) + return false; + if (index >= MAX_COUNT) + return false; + + return occupied[index]; +} + +void KernelObjectPool::Clear() +{ + for (int i = 0; i < MAX_COUNT; i++) + { + //brutally clear everything, no validation + if (occupied[i]) + delete pool[i]; + occupied[i] = false; + } + memset(pool, 0, sizeof(KernelObject*)*MAX_COUNT); + next_id = INITIAL_NEXT_ID; +} + +KernelObject *&KernelObjectPool::operator [](UID handle) +{ + _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); + return pool[handle - HANDLE_OFFSET]; +} + +void KernelObjectPool::List() { + for (int i = 0; i < MAX_COUNT; i++) { + if (occupied[i]) { + if (pool[i]) { + INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName(), + pool[i]->GetName()); + } + } + } +} + +int KernelObjectPool::GetCount() +{ + int count = 0; + for (int i = 0; i < MAX_COUNT; i++) + { + if (occupied[i]) + count++; + } + return count; +} + +KernelObject *KernelObjectPool::CreateByIDType(int type) { + // Used for save states. This is ugly, but what other way is there? + switch (type) { + //case SCE_KERNEL_TMID_Alarm: + // return __KernelAlarmObject(); + //case SCE_KERNEL_TMID_EventFlag: + // return __KernelEventFlagObject(); + //case SCE_KERNEL_TMID_Mbox: + // return __KernelMbxObject(); + //case SCE_KERNEL_TMID_Fpl: + // return __KernelMemoryFPLObject(); + //case SCE_KERNEL_TMID_Vpl: + // return __KernelMemoryVPLObject(); + //case PPSSPP_KERNEL_TMID_PMB: + // return __KernelMemoryPMBObject(); + //case PPSSPP_KERNEL_TMID_Module: + // return __KernelModuleObject(); + //case SCE_KERNEL_TMID_Mpipe: + // return __KernelMsgPipeObject(); + //case SCE_KERNEL_TMID_Mutex: + // return __KernelMutexObject(); + //case SCE_KERNEL_TMID_LwMutex: + // return __KernelLwMutexObject(); + //case SCE_KERNEL_TMID_Semaphore: + // return __KernelSemaphoreObject(); + //case SCE_KERNEL_TMID_Callback: + // return __KernelCallbackObject(); + //case SCE_KERNEL_TMID_Thread: + // return __KernelThreadObject(); + //case SCE_KERNEL_TMID_VTimer: + // return __KernelVTimerObject(); + //case SCE_KERNEL_TMID_Tlspl: + // return __KernelTlsplObject(); + //case PPSSPP_KERNEL_TMID_File: + // return __KernelFileNodeObject(); + //case PPSSPP_KERNEL_TMID_DirList: + // return __KernelDirListingObject(); + + default: + ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type); + return NULL; + } +} diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h new file mode 100644 index 0000000000..2381ca7f74 --- /dev/null +++ b/src/core/hle/kernel/kernel.h @@ -0,0 +1,121 @@ +// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +typedef u32 UID; + +class KernelObjectPool; + +class KernelObject { + friend class KernelObjectPool; + u32 uid; +public: + virtual ~KernelObject() {} + UID GetUID() const { return uid; } + virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; } + virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; } + virtual int GetIDType() const = 0; + //virtual void GetQuickInfo(char *ptr, int size); +}; + +class KernelObjectPool { +public: + KernelObjectPool(); + ~KernelObjectPool() {} + + // Allocates a UID within the range and inserts the object into the map. + UID Create(KernelObject *obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); + + static KernelObject *CreateByIDType(int type); + + template + u32 Destroy(UID handle) { + u32 error; + if (Get(handle, error)) { + occupied[handle - handleOffset] = false; + delete pool[handle - handleOffset]; + } + return error; + }; + + bool IsValid(UID handle); + + template + T* Get(UID handle, u32& outError) { + if (handle < handleOffset || handle >= handleOffset + maxCount || !occupied[handle - handleOffset]) { + // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP + if (handle != 0 && (u32)handle != 0x80020001) { + WARN_LOG(SCEKERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); + } + outError = T::GetMissingErrorCode(); + return 0; + } else { + // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally, + // it just acted as a static case and everything worked. This means that we will never + // see the Wrong type object error below, but we'll just have to live with that danger. + T* t = static_cast(pool[handle - handleOffset]); + if (t == 0 || t->GetIDType() != T::GetStaticIDType()) { + WARN_LOG(SCEKERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); + outError = T::GetMissingErrorCode(); + return 0; + } + outError = SCE_KERNEL_ERROR_OK; + return t; + } + } + + // ONLY use this when you know the handle is valid. + template + T *GetFast(UID handle) { + const UID realHandle = handle - handleOffset; + _dbg_assert_(SCEKERNEL, realHandle >= 0 && realHandle < maxCount && occupied[realHandle]); + return static_cast(pool[realHandle]); + } + + template + void Iterate(bool func(T *, ArgT), ArgT arg) { + int type = T::GetStaticIDType(); + for (int i = 0; i < maxCount; i++) + { + if (!occupied[i]) + continue; + T *t = static_cast(pool[i]); + if (t->GetIDType() == type) { + if (!func(t, arg)) + break; + } + } + } + + bool GetIDType(UID handle, int *type) const { + if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || + !occupied[handle - HANDLE_OFFSET]) { + ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); + return false; + } + KernelObject *t = pool[handle - HANDLE_OFFSET]; + *type = t->GetIDType(); + return true; + } + + KernelObject *&operator [](UID handle); + void List(); + void Clear(); + int GetCount(); + +private: + enum { + MAX_COUNT = 0x1000, + HANDLE_OFFSET = 0x100, + INITIAL_NEXT_ID = 0x10, + }; + KernelObject *pool[MAX_COUNT]; + bool occupied[MAX_COUNT]; + int next_id; +}; + +extern KernelObjectPool g_kernel_objects; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp new file mode 100644 index 0000000000..0ed35de83f --- /dev/null +++ b/src/core/hle/kernel/thread.cpp @@ -0,0 +1,228 @@ +// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#include +#include +#include +#include + +#include "common/common.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/thread.h" + +// Real CTR struct, don't change the fields. +struct NativeThread { + //u32 Pointer to vtable + //u32 Reference count + //KProcess* Process the thread belongs to (virtual address) + //u32 Thread id + //u32* ptr = *(KThread+0x8C) - 0xB0 + //u32* End-address of the page for this thread allocated in the 0xFF4XX000 region. Thus, + // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000. + //KThread* Previous ? (virtual address) + //KThread* Next ? (virtual address) +}; + +struct ThreadWaitInfo { + u32 wait_value; + u32 timeout_ptr; +}; + +class Thread : public KernelObject { +public: + /*const char *GetName() { return nt.name; }*/ + const char *GetTypeName() { return "Thread"; } + //void GetQuickInfo(char *ptr, int size) + //{ + // sprintf(ptr, "pc= %08x sp= %08x %s %s %s %s %s %s (wt=%i wid=%i wv= %08x )", + // context.pc, context.r[13], // 13 is stack pointer + // (nt.status & THREADSTATUS_RUNNING) ? "RUN" : "", + // (nt.status & THREADSTATUS_READY) ? "READY" : "", + // (nt.status & THREADSTATUS_WAIT) ? "WAIT" : "", + // (nt.status & THREADSTATUS_SUSPEND) ? "SUSPEND" : "", + // (nt.status & THREADSTATUS_DORMANT) ? "DORMANT" : "", + // (nt.status & THREADSTATUS_DEAD) ? "DEAD" : "", + // nt.waitType, + // nt.waitID, + // waitInfo.waitValue); + //} + + //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } + //static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; } + //int GetIDType() const { return SCE_KERNEL_TMID_Thread; } + + //bool AllocateStack(u32 &stack_size) { + // FreeStack(); + + // bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0; + // if (nt.attr & PSP_THREAD_ATTR_KERNEL) + // { + // // Allocate stacks for kernel threads (idle) in kernel RAM + // currentStack.start = kernelMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); + // } + // else + // { + // currentStack.start = userMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); + // } + // if (currentStack.start == (u32)-1) + // { + // currentStack.start = 0; + // nt.initialStack = 0; + // ERROR_LOG(KERNEL, "Failed to allocate stack for thread"); + // return false; + // } + + // nt.initialStack = currentStack.start; + // nt.stack_size = stack_size; + // return true; + //} + + //bool FillStack() { + // // Fill the stack. + // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { + // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); + // } + // context.r[MIPS_REG_SP] = currentStack.start + nt.stack_size; + // currentStack.end = context.r[MIPS_REG_SP]; + // // The k0 section is 256 bytes at the top of the stack. + // context.r[MIPS_REG_SP] -= 256; + // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; + // u32 k0 = context.r[MIPS_REG_K0]; + // Memory::Memset(k0, 0, 0x100); + // Memory::Write_U32(GetUID(), k0 + 0xc0); + // Memory::Write_U32(nt.initialStack, k0 + 0xc8); + // Memory::Write_U32(0xffffffff, k0 + 0xf8); + // Memory::Write_U32(0xffffffff, k0 + 0xfc); + // // After k0 comes the arguments, which is done by sceKernelStartThread(). + + // Memory::Write_U32(GetUID(), nt.initialStack); + // return true; + //} + + //void FreeStack() { + // if (currentStack.start != 0) { + // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name); + + // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { + // Memory::Memset(nt.initialStack, 0, nt.stack_size); + // } + + // if (nt.attr & PSP_THREAD_ATTR_KERNEL) { + // kernelMemory.Free(currentStack.start); + // } + // else { + // userMemory.Free(currentStack.start); + // } + // currentStack.start = 0; + // } + //} + + //bool PushExtendedStack(u32 size) { + // u32 stack = userMemory.Alloc(size, true, (std::string("extended/") + nt.name).c_str()); + // if (stack == (u32)-1) + // return false; + + // pushed_stacks.push_back(currentStack); + // currentStack.start = stack; + // currentStack.end = stack + size; + // nt.initialStack = currentStack.start; + // nt.stack_size = currentStack.end - currentStack.start; + + // // We still drop the threadID at the bottom and fill it, but there's no k0. + // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); + // Memory::Write_U32(GetUID(), nt.initialStack); + // return true; + //} + + //bool PopExtendedStack() { + // if (pushed_stacks.size() == 0) { + // return false; + // } + // userMemory.Free(currentStack.start); + // currentStack = pushed_stacks.back(); + // pushed_stacks.pop_back(); + // nt.initialStack = currentStack.start; + // nt.stack_size = currentStack.end - currentStack.start; + // return true; + //} + + Thread() { + currentStack.start = 0; + } + + // Can't use a destructor since savestates will call that too. + //void Cleanup() { + // // Callbacks are automatically deleted when their owning thread is deleted. + // for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) + // kernelObjects.Destroy(*it); + + // if (pushed_stacks.size() != 0) + // { + // WARN_LOG(KERNEL, "Thread ended within an extended stack"); + // for (size_t i = 0; i < pushed_stacks.size(); ++i) + // userMemory.Free(pushed_stacks[i].start); + // } + // FreeStack(); + //} + + void setReturnValue(u32 retval); + void setReturnValue(u64 retval); + void resumeFromWait(); + //bool isWaitingFor(WaitType type, int id); + //int getWaitID(WaitType type); + ThreadWaitInfo getWaitInfo(); + + // Utils + //inline bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } + //inline bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } + //inline bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; } + //inline bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } + //inline bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } + + NativeThread nt; + + ThreadWaitInfo waitInfo; + UID moduleId; + + bool isProcessingCallbacks; + u32 currentMipscallId; + UID currentCallbackId; + + ThreadContext context; + + std::vector callbacks; + + std::list pending_calls; + + struct StackInfo { + u32 start; + u32 end; + }; + // This is a stack of... stacks, since sceKernelExtendThreadStack() can recurse. + // These are stacks that aren't "active" right now, but will pop off once the func returns. + std::vector pushed_stacks; + + StackInfo currentStack; + + // For thread end. + std::vector waiting_threads; + // Key is the callback id it was for, or if no callback, the thread id. + std::map paused_waits; +}; + +void __KernelThreadingInit() { +} + +void __KernelThreadingShutdown() { +} + +//const char *__KernelGetThreadName(UID threadID); +// +//void __KernelSaveContext(ThreadContext *ctx); +//void __KernelLoadContext(ThreadContext *ctx); + +//void __KernelSwitchContext(Thread *target, const char *reason); \ No newline at end of file diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h new file mode 100644 index 0000000000..c3cdca31f4 --- /dev/null +++ b/src/core/hle/kernel/thread.h @@ -0,0 +1,36 @@ +// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +enum ThreadStatus { + THREADSTATUS_RUNNING = 1, + THREADSTATUS_READY = 2, + THREADSTATUS_WAIT = 4, + THREADSTATUS_SUSPEND = 8, + THREADSTATUS_DORMANT = 16, + THREADSTATUS_DEAD = 32, + + THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND +}; + +struct ThreadContext { + void reset(); + + u32 reg[16]; + u32 cpsr; + u32 pc; +}; + +void __KernelThreadingInit(); +void __KernelThreadingShutdown(); + +//const char *__KernelGetThreadName(SceUID threadID); +// +//void __KernelSaveContext(ThreadContext *ctx); +//void __KernelLoadContext(ThreadContext *ctx); + +//void __KernelSwitchContext(Thread *target, const char *reason); \ No newline at end of file From ce4d271a53e79814f66a46bd69a8970ea3849ee9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 11 May 2014 22:14:13 -0400 Subject: [PATCH 06/72] added option to set CPSR register to arm_interface --- src/core/arm/arm_interface.h | 6 ++++++ src/core/arm/interpreter/arm_interpreter.cpp | 8 ++++++++ src/core/arm/interpreter/arm_interpreter.h | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 4dfe0570b5..602c91e302 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -55,6 +55,12 @@ public: */ virtual u32 GetCPSR() const = 0; + /** + * Set the current CPSR register + * @param cpsr Value to set CPSR to + */ + virtual void SetCPSR(u32 cpsr) = 0; + /** * Returns the number of clock ticks since the last rese * @return Returns number of clock ticks diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp index 4045779d78..81147f2d47 100644 --- a/src/core/arm/interpreter/arm_interpreter.cpp +++ b/src/core/arm/interpreter/arm_interpreter.cpp @@ -77,6 +77,14 @@ u32 ARM_Interpreter::GetCPSR() const { return m_state->Cpsr; } +/** + * Set the current CPSR register + * @param cpsr Value to set CPSR to + */ +void ARM_Interpreter::SetCPSR(u32 cpsr) { + m_state->Cpsr = cpsr; +} + /** * Returns the number of clock ticks since the last reset * @return Returns number of clock ticks diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h index 625c0c652c..78b188bee4 100644 --- a/src/core/arm/interpreter/arm_interpreter.h +++ b/src/core/arm/interpreter/arm_interpreter.h @@ -48,6 +48,12 @@ public: */ u32 GetCPSR() const; + /** + * Set the current CPSR register + * @param cpsr Value to set CPSR to + */ + void SetCPSR(u32 cpsr); + /** * Returns the number of clock ticks since the last reset * @return Returns number of clock ticks From 1583d2b6f32ec6f558646284a8130c0759e93e12 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 21:57:12 -0400 Subject: [PATCH 07/72] - added __KernelLoadExec function - fixed some logging --- src/core/hle/kernel/kernel.cpp | 11 ++++++++++ src/core/hle/kernel/kernel.h | 37 ++++++++++++++++++++++------------ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index fc494fe30f..fb6ae8495a 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -8,6 +8,7 @@ #include "common/common.h" +#include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" @@ -140,3 +141,13 @@ KernelObject *KernelObjectPool::CreateByIDType(int type) { return NULL; } } + +bool __KernelLoadExec(u32 entry_point) { + __KernelInit(); + + Core::g_app_core->SetPC(entry_point); + + UID thread_id = __KernelSetupRootThread(0xDEADBEEF, 0, 0x31); + + return true; +} diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2381ca7f74..0eb58210cc 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -8,6 +8,15 @@ typedef u32 UID; +enum KernelIDType { + KERNEL_ID_TYPE_THREAD = 1, + KERNEL_ID_TYPE_SEMAPHORE = 2, + KERNEL_ID_TYPE_MUTEX = 3, + KERNEL_ID_TYPE_EVENT = 4, +}; + +#define KERNELOBJECT_MAX_NAME_LENGTH 31 + class KernelObjectPool; class KernelObject { @@ -18,7 +27,7 @@ public: UID GetUID() const { return uid; } virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; } virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; } - virtual int GetIDType() const = 0; + virtual KernelIDType GetIDType() const = 0; //virtual void GetQuickInfo(char *ptr, int size); }; @@ -36,8 +45,8 @@ public: u32 Destroy(UID handle) { u32 error; if (Get(handle, error)) { - occupied[handle - handleOffset] = false; - delete pool[handle - handleOffset]; + occupied[handle - HANDLE_OFFSET] = false; + delete pool[handle - HANDLE_OFFSET]; } return error; }; @@ -46,24 +55,24 @@ public: template T* Get(UID handle, u32& outError) { - if (handle < handleOffset || handle >= handleOffset + maxCount || !occupied[handle - handleOffset]) { + if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP if (handle != 0 && (u32)handle != 0x80020001) { - WARN_LOG(SCEKERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); + WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); } - outError = T::GetMissingErrorCode(); + outError = 0;//T::GetMissingErrorCode(); return 0; } else { // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally, // it just acted as a static case and everything worked. This means that we will never // see the Wrong type object error below, but we'll just have to live with that danger. - T* t = static_cast(pool[handle - handleOffset]); + T* t = static_cast(pool[handle - HANDLE_OFFSET]); if (t == 0 || t->GetIDType() != T::GetStaticIDType()) { - WARN_LOG(SCEKERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); - outError = T::GetMissingErrorCode(); + WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); + outError = 0;//T::GetMissingErrorCode(); return 0; } - outError = SCE_KERNEL_ERROR_OK; + outError = 0;//SCE_KERNEL_ERROR_OK; return t; } } @@ -71,15 +80,15 @@ public: // ONLY use this when you know the handle is valid. template T *GetFast(UID handle) { - const UID realHandle = handle - handleOffset; - _dbg_assert_(SCEKERNEL, realHandle >= 0 && realHandle < maxCount && occupied[realHandle]); + const UID realHandle = handle - HANDLE_OFFSET; + _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); return static_cast(pool[realHandle]); } template void Iterate(bool func(T *, ArgT), ArgT arg) { int type = T::GetStaticIDType(); - for (int i = 0; i < maxCount; i++) + for (int i = 0; i < MAX_COUNT; i++) { if (!occupied[i]) continue; @@ -119,3 +128,5 @@ private: }; extern KernelObjectPool g_kernel_objects; + +bool __KernelLoadExec(u32 entry_point); From b5ef630c9c3133c1452462420788076e9890bcc3 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 21:58:26 -0400 Subject: [PATCH 08/72] added CreateThread, CreateMutex, and ReleaseMutex SVC stubs (just parameter decoding for now) --- src/core/hle/function_wrappers.h | 2 +- src/core/hle/syscall.cpp | 43 +++++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 18b01b14bb..83be7648b2 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -740,7 +740,7 @@ template void WrapI_VUVI(){ } template void WrapI_VUUUUU(){ - u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5)); + u32 retval = func(NULL, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); RETURN(retval); } diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index d47df6038a..c697bc277c 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -10,6 +10,8 @@ #include "core/hle/syscall.h" #include "core/hle/service/service.h" +#include "common/symbols.h" + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Syscall @@ -26,7 +28,8 @@ enum MapMemoryPermission { }; /// Map application or GSP heap memory -Result ControlMemory(u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { +Result ControlMemory(void* _outaddr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { + u32* outaddr = (u32*)_outaddr; u32 virtual_address = 0x00000000; DEBUG_LOG(SVC, "ControlMemory called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", @@ -48,7 +51,9 @@ Result ControlMemory(u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissi default: ERROR_LOG(SVC, "ControlMemory unknown operation=0x%08X", operation); } - + if (NULL != outaddr) { + *outaddr = virtual_address; + } Core::g_app_core->SetReg(1, virtual_address); return 0; @@ -134,16 +139,42 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* return 0; } +Result CreateThread(void* thread, u32 threadpriority, u32 entrypoint, u32 arg, u32 stacktop, u32 processorid) { + std::string symbol_name = "unknown"; + if (Symbols::HasSymbol(entrypoint)) { + TSymbol symbol = Symbols::GetSymbol(entrypoint); + symbol_name = symbol.name; + } + // stack top: 0x0056A4A0 + DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, " + "stacktop=0x%08X, threadpriority=0x%08X, processorid=0x%08X", entrypoint, + symbol_name.c_str(), arg, stacktop, threadpriority, processorid); + + return 0; +} + +Result CreateMutex(void* _mutex, u32 initial_locked) { + Handle* mutex = (Handle*)_mutex; + DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateMutex called initial_locked=%s", + initial_locked ? "true" : "false"); + return 0; +} + +Result ReleaseMutex(Handle handle) { + DEBUG_LOG(SVC, "(UNIMPLEMENTED) ReleaseMutex called handle=0x%08X", handle); + return 0; +} + const HLE::FunctionDef Syscall_Table[] = { {0x00, NULL, "Unknown"}, - {0x01, WrapI_UUUUU, "ControlMemory"}, + {0x01, WrapI_VUUUUU, "ControlMemory"}, {0x02, NULL, "QueryMemory"}, {0x03, NULL, "ExitProcess"}, {0x04, NULL, "GetProcessAffinityMask"}, {0x05, NULL, "SetProcessAffinityMask"}, {0x06, NULL, "GetProcessIdealProcessor"}, {0x07, NULL, "SetProcessIdealProcessor"}, - {0x08, NULL, "CreateThread"}, + {0x08, WrapI_VUUUUU, "CreateThread"}, {0x09, NULL, "ExitThread"}, {0x0A, NULL, "SleepThread"}, {0x0B, NULL, "GetThreadPriority"}, @@ -154,8 +185,8 @@ const HLE::FunctionDef Syscall_Table[] = { {0x10, NULL, "SetThreadIdealProcessor"}, {0x11, NULL, "GetCurrentProcessorNumber"}, {0x12, NULL, "Run"}, - {0x13, NULL, "CreateMutex"}, - {0x14, NULL, "ReleaseMutex"}, + {0x13, WrapI_VU, "CreateMutex"}, + {0x14, WrapI_U, "ReleaseMutex"}, {0x15, NULL, "CreateSemaphore"}, {0x16, NULL, "ReleaseSemaphore"}, {0x17, NULL, "CreateEvent"}, From b87536e82cc1708d22ce4e38b34dac8190919798 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 21:58:51 -0400 Subject: [PATCH 09/72] changed loader to use __KernelLoadExec --- src/core/loader.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/core/loader.cpp b/src/core/loader.cpp index 8756588aec..a1f5dcdb59 100644 --- a/src/core/loader.cpp +++ b/src/core/loader.cpp @@ -10,7 +10,7 @@ #include "core/core.h" #include "core/file_sys/directory_file_system.h" #include "core/elf/elf_reader.h" - +#include "core/hle/kernel/kernel.h" #include "core/mem_map.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -56,7 +56,7 @@ bool Load_ELF(std::string &filename) { elf_reader = new ElfReader(buffer); elf_reader->LoadInto(0x00100000); - Core::g_app_core->SetPC(elf_reader->GetEntryPoint()); + __KernelLoadExec(elf_reader->GetEntryPoint()); delete[] buffer; delete elf_reader; @@ -89,11 +89,11 @@ bool Load_DAT(std::string &filename) { * but for the sake of making it easier... we'll temporarily/hackishly * allow it. No sense in making a proper reader for this. */ - u32 entrypoint = 0x00100000; // write to same entrypoint as elf + u32 entry_point = 0x00100000; // write to same entrypoint as elf u32 payload_offset = 0xA150; const u8 *src = &buffer[payload_offset]; - u8 *dst = Memory::GetPointer(entrypoint); + u8 *dst = Memory::GetPointer(entry_point); u32 srcSize = size - payload_offset; //just load everything... u32 *s = (u32*)src; u32 *d = (u32*)dst; @@ -102,7 +102,8 @@ bool Load_DAT(std::string &filename) { *d++ = (*s++); } - Core::g_app_core->SetPC(entrypoint); + __KernelLoadExec(entry_point); + delete[] buffer; } @@ -131,10 +132,10 @@ bool Load_BIN(std::string &filename) { f.ReadBytes(buffer, size); - u32 entrypoint = 0x00100000; // Hardcoded, read from exheader + u32 entry_point = 0x00100000; // Hardcoded, read from exheader const u8 *src = buffer; - u8 *dst = Memory::GetPointer(entrypoint); + u8 *dst = Memory::GetPointer(entry_point); u32 srcSize = size; u32 *s = (u32*)src; u32 *d = (u32*)dst; @@ -143,7 +144,7 @@ bool Load_BIN(std::string &filename) { *d++ = (*s++); } - Core::g_app_core->SetPC(entrypoint); + __KernelLoadExec(entry_point); delete[] buffer; } From 3838d46b9022964617b93a45f3feab5052c3538b Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 22:00:11 -0400 Subject: [PATCH 10/72] added a bunch of threading code, recycled from PPSSPP, with lots of hacks in for 3DS... doesn't really do much yet. Just a jumping off point --- src/core/hle/kernel/thread.cpp | 586 +++++++++++++++++++++++++++++---- src/core/hle/kernel/thread.h | 25 +- 2 files changed, 542 insertions(+), 69 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 0ed35de83f..584276eecb 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -11,10 +11,212 @@ #include "common/common.h" +#include "core/core.h" +#include "core/mem_map.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -// Real CTR struct, don't change the fields. +struct ThreadQueueList { + // Number of queues (number of priority levels starting at 0.) + static const int NUM_QUEUES = 128; + // Initial number of threads a single queue can handle. + static const int INITIAL_CAPACITY = 32; + + struct Queue { + // Next ever-been-used queue (worse priority.) + Queue *next; + // First valid item in data. + int first; + // One after last valid item in data. + int end; + // A too-large array with room on the front and end. + UID *data; + // Size of data array. + int capacity; + }; + + ThreadQueueList() { + memset(queues, 0, sizeof(queues)); + first = invalid(); + } + + ~ThreadQueueList() { + for (int i = 0; i < NUM_QUEUES; ++i) { + if (queues[i].data != NULL) { + free(queues[i].data); + } + } + } + + // Only for debugging, returns priority level. + int contains(const UID uid) { + for (int i = 0; i < NUM_QUEUES; ++i) { + if (queues[i].data == NULL) { + continue; + } + Queue *cur = &queues[i]; + for (int j = cur->first; j < cur->end; ++j) { + if (cur->data[j] == uid) { + return i; + } + } + } + return -1; + } + + inline UID pop_first() { + Queue *cur = first; + while (cur != invalid()) { + if (cur->end - cur->first > 0) { + return cur->data[cur->first++]; + } + cur = cur->next; + } + + _dbg_assert_msg_(KERNEL, false, "ThreadQueueList should not be empty."); + return 0; + } + + inline UID pop_first_better(u32 priority) { + Queue *cur = first; + Queue *stop = &queues[priority]; + while (cur < stop) { + if (cur->end - cur->first > 0) { + return cur->data[cur->first++]; + } + cur = cur->next; + } + return 0; + } + + inline void push_front(u32 priority, const UID thread_id) { + Queue *cur = &queues[priority]; + cur->data[--cur->first] = thread_id; + if (cur->first == 0) { + rebalance(priority); + } + } + + inline void push_back(u32 priority, const UID thread_id) + { + Queue *cur = &queues[priority]; + cur->data[cur->end++] = thread_id; + if (cur->end == cur->capacity) { + rebalance(priority); + } + } + + inline void remove(u32 priority, const UID thread_id) { + Queue *cur = &queues[priority]; + _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); + + for (int i = cur->first; i < cur->end; ++i) { + if (cur->data[i] == thread_id) { + int remaining = --cur->end - i; + if (remaining > 0) { + memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(UID)); + } + return; + } + } + + // Wasn't there. + } + + inline void rotate(u32 priority) { + Queue *cur = &queues[priority]; + _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); + + if (cur->end - cur->first > 1) { + cur->data[cur->end++] = cur->data[cur->first++]; + if (cur->end == cur->capacity) { + rebalance(priority); + } + } + } + + inline void clear() { + for (int i = 0; i < NUM_QUEUES; ++i) { + if (queues[i].data != NULL) { + free(queues[i].data); + } + } + memset(queues, 0, sizeof(queues)); + first = invalid(); + } + + inline bool empty(u32 priority) const { + const Queue *cur = &queues[priority]; + return cur->first == cur->end; + } + + inline void prepare(u32 priority) { + Queue *cur = &queues[priority]; + if (cur->next == NULL) { + link(priority, INITIAL_CAPACITY); + } + } + +private: + Queue *invalid() const { + return (Queue *)-1; + } + + void link(u32 priority, int size) { + _dbg_assert_msg_(KERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once."); + + if (size <= INITIAL_CAPACITY) { + size = INITIAL_CAPACITY; + } else { + int goal = size; + size = INITIAL_CAPACITY; + while (size < goal) + size *= 2; + } + Queue *cur = &queues[priority]; + cur->data = (UID*)malloc(sizeof(UID)* size); + cur->capacity = size; + cur->first = size / 2; + cur->end = size / 2; + + for (int i = (int)priority - 1; i >= 0; --i) { + if (queues[i].next != NULL) { + cur->next = queues[i].next; + queues[i].next = cur; + return; + } + } + + cur->next = first; + first = cur; + } + + void rebalance(u32 priority) { + Queue *cur = &queues[priority]; + int size = cur->end - cur->first; + if (size >= cur->capacity - 2) { + UID* new_data = (UID*)realloc(cur->data, cur->capacity * 2 * sizeof(UID)); + if (new_data != NULL) { + cur->capacity *= 2; + cur->data = new_data; + } + } + + int newFirst = (cur->capacity - size) / 2; + if (newFirst != cur->first) { + memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(UID)); + cur->first = newFirst; + cur->end = newFirst + size; + } + } + + // The first queue that's ever been used. + Queue* first; + // The priority level queues of thread ids. + Queue queues[NUM_QUEUES]; +}; + +// Supposed to represent a real CTR struct... but not sure of the correct fields yet. struct NativeThread { //u32 Pointer to vtable //u32 Reference count @@ -25,6 +227,22 @@ struct NativeThread { // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000. //KThread* Previous ? (virtual address) //KThread* Next ? (virtual address) + + u32_le native_size; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; + + // Threading stuff + u32_le status; + u32_le entry_point; + u32_le initial_stack; + u32_le stack_top; + u32_le stack_size; + + u32_le arg; + u32_le processor_id; + + s32_le initial_priority; + s32_le current_priority; }; struct ThreadWaitInfo { @@ -52,42 +270,23 @@ public: //} //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } - //static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; } - //int GetIDType() const { return SCE_KERNEL_TMID_Thread; } + static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } + KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } - //bool AllocateStack(u32 &stack_size) { - // FreeStack(); - - // bool fromTop = (nt.attr & PSP_THREAD_ATTR_LOW_STACK) == 0; - // if (nt.attr & PSP_THREAD_ATTR_KERNEL) - // { - // // Allocate stacks for kernel threads (idle) in kernel RAM - // currentStack.start = kernelMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); - // } - // else - // { - // currentStack.start = userMemory.Alloc(stack_size, fromTop, (std::string("stack/") + nt.name).c_str()); - // } - // if (currentStack.start == (u32)-1) - // { - // currentStack.start = 0; - // nt.initialStack = 0; - // ERROR_LOG(KERNEL, "Failed to allocate stack for thread"); - // return false; - // } - - // nt.initialStack = currentStack.start; - // nt.stack_size = stack_size; - // return true; - //} + bool SetupStack(u32 stack_top, int stack_size) { + current_stack.start = stack_top; + nt.initial_stack = current_stack.start; + nt.stack_size = stack_size; + return true; + } //bool FillStack() { // // Fill the stack. // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { - // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); + // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); // } - // context.r[MIPS_REG_SP] = currentStack.start + nt.stack_size; - // currentStack.end = context.r[MIPS_REG_SP]; + // context.r[MIPS_REG_SP] = current_stack.start + nt.stack_size; + // current_stack.end = context.r[MIPS_REG_SP]; // // The k0 section is 256 bytes at the top of the stack. // context.r[MIPS_REG_SP] -= 256; // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; @@ -104,7 +303,7 @@ public: //} //void FreeStack() { - // if (currentStack.start != 0) { + // if (current_stack.start != 0) { // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name); // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { @@ -112,12 +311,12 @@ public: // } // if (nt.attr & PSP_THREAD_ATTR_KERNEL) { - // kernelMemory.Free(currentStack.start); + // kernelMemory.Free(current_stack.start); // } // else { - // userMemory.Free(currentStack.start); + // userMemory.Free(current_stack.start); // } - // currentStack.start = 0; + // current_stack.start = 0; // } //} @@ -126,14 +325,14 @@ public: // if (stack == (u32)-1) // return false; - // pushed_stacks.push_back(currentStack); - // currentStack.start = stack; - // currentStack.end = stack + size; - // nt.initialStack = currentStack.start; - // nt.stack_size = currentStack.end - currentStack.start; + // pushed_stacks.push_back(current_stack); + // current_stack.start = stack; + // current_stack.end = stack + size; + // nt.initialStack = current_stack.start; + // nt.stack_size = current_stack.end - current_stack.start; - // // We still drop the threadID at the bottom and fill it, but there's no k0. - // Memory::Memset(currentStack.start, 0xFF, nt.stack_size); + // // We still drop the thread_id at the bottom and fill it, but there's no k0. + // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); // Memory::Write_U32(GetUID(), nt.initialStack); // return true; //} @@ -142,16 +341,16 @@ public: // if (pushed_stacks.size() == 0) { // return false; // } - // userMemory.Free(currentStack.start); - // currentStack = pushed_stacks.back(); + // userMemory.Free(current_stack.start); + // current_stack = pushed_stacks.back(); // pushed_stacks.pop_back(); - // nt.initialStack = currentStack.start; - // nt.stack_size = currentStack.end - currentStack.start; + // nt.initialStack = current_stack.start; + // nt.stack_size = current_stack.end - current_stack.start; // return true; //} Thread() { - currentStack.start = 0; + current_stack.start = 0; } // Can't use a destructor since savestates will call that too. @@ -177,20 +376,20 @@ public: ThreadWaitInfo getWaitInfo(); // Utils - //inline bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } - //inline bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } - //inline bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; } - //inline bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } - //inline bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } + inline bool IsRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } + inline bool IsStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } + inline bool IsReady() const { return (nt.status & THREADSTATUS_READY) != 0; } + inline bool IsWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } + inline bool IsSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } NativeThread nt; ThreadWaitInfo waitInfo; UID moduleId; - bool isProcessingCallbacks; - u32 currentMipscallId; - UID currentCallbackId; + //bool isProcessingCallbacks; + //u32 currentMipscallId; + //UID currentCallbackId; ThreadContext context; @@ -206,7 +405,7 @@ public: // These are stacks that aren't "active" right now, but will pop off once the func returns. std::vector pushed_stacks; - StackInfo currentStack; + StackInfo current_stack; // For thread end. std::vector waiting_threads; @@ -214,15 +413,276 @@ public: std::map paused_waits; }; +void ThreadContext::reset() { + for (int i = 0; i < 16; i++) { + reg[i] = 0; + } + reg[13] = Memory::SCRATCHPAD_VADDR_END; + cpsr = 0; +} + +// Lists all thread ids that aren't deleted/etc. +std::vector g_thread_queue; + +// Lists only ready thread ids +ThreadQueueList g_thread_ready_queue; + +UID g_current_thread; +Thread* g_current_thread_ptr; +const char *g_hle_current_thread_name = NULL; + +Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, + u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { + + Thread *t = new Thread; + id = g_kernel_objects.Create(t); + + g_thread_queue.push_back(id); + g_thread_ready_queue.prepare(priority); + + memset(&t->nt, 0xCD, sizeof(t->nt)); + + t->nt.entry_point = entrypoint; + t->nt.native_size = sizeof(t->nt); + t->nt.initial_priority = t->nt.current_priority = priority; + t->nt.status = THREADSTATUS_DORMANT; + t->nt.initial_stack = t->nt.stack_top = stack_top; + t->nt.stack_size = stack_size; + t->nt.processor_id = processor_id; + + strncpy(t->nt.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + t->nt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + + t->nt.stack_size = stack_size; + t->SetupStack(stack_top, stack_size); + + return t; +} + +void __KernelResetThread(Thread *t, int lowest_priority) { + t->context.reset(); + t->context.pc = t->nt.entry_point; + + // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... + if (t->nt.current_priority < lowest_priority) + t->nt.current_priority = t->nt.initial_priority; + + //t->nt.wait_type = WAITTYPE_NONE; + //t->nt.wait_id = 0; + memset(&t->waitInfo, 0, sizeof(t->waitInfo)); + + //t->nt.exitStatus = SCE_KERNEL_ERROR_NOT_DORMANT; + //t->isProcessingCallbacks = false; + //t->currentCallbackId = 0; + //t->currentMipscallId = 0; + //t->pendingMipsCalls.clear(); + + //t->context.r[MIPS_REG_RA] = threadReturnHackAddr; //hack! TODO fix + // TODO: Not sure if it's reset here, but this makes sense. + //t->context.r[MIPS_REG_GP] = t->nt.gpreg; + //t->FillStack(); + + //if (!t->waitingThreads.empty()) + // ERROR_LOG(KERNEL, "Resetting thread with threads waiting on end?"); +} + + +inline Thread *__GetCurrentThread() { + return g_current_thread_ptr; +} + +inline void __SetCurrentThread(Thread *thread, UID thread_id, const char *name) { + g_current_thread = thread_id; + g_current_thread_ptr = thread; + g_hle_current_thread_name = name; +} + +// TODO: Use __KernelChangeThreadState instead? It has other affects... +void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready) { + // Passing the id as a parameter is just an optimization, if it's wrong it will cause havoc. + _dbg_assert_msg_(KERNEL, thread->GetUID() == thread_id, "Incorrect thread_id"); + int prio = thread->nt.current_priority; + + if (thread->IsReady()) { + if (!ready) + g_thread_ready_queue.remove(prio, thread_id); + } else if (ready) { + if (thread->IsRunning()) { + g_thread_ready_queue.push_front(prio, thread_id); + } else { + g_thread_ready_queue.push_back(prio, thread_id); + } + thread->nt.status = THREADSTATUS_READY; + } +} + +void __KernelChangeReadyState(UID thread_id, bool ready) { + u32 error; + Thread *thread = g_kernel_objects.Get(thread_id, error); + if (thread) { + __KernelChangeReadyState(thread, thread_id, ready); + } else { + WARN_LOG(KERNEL, "Trying to change the ready state of an unknown thread?"); + } +} + +// Returns NULL if the current thread is fine. +Thread* __KernelNextThread() { + UID bestThread; + + // If the current thread is running, it's a valid candidate. + Thread *cur = __GetCurrentThread(); + if (cur && cur->IsRunning()) { + bestThread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); + if (bestThread != 0) { + __KernelChangeReadyState(cur, g_current_thread, true); + } + } else { + bestThread = g_thread_ready_queue.pop_first(); + } + + // Assume g_thread_ready_queue has not become corrupt. + if (bestThread != 0) { + return g_kernel_objects.GetFast(bestThread); + } else { + return NULL; + } +} + +// Saves the current CPU context +void __KernelSaveContext(ThreadContext *ctx) { + ctx->reg[0] = Core::g_app_core->GetReg(0); + ctx->reg[1] = Core::g_app_core->GetReg(1); + ctx->reg[2] = Core::g_app_core->GetReg(2); + ctx->reg[3] = Core::g_app_core->GetReg(3); + ctx->reg[4] = Core::g_app_core->GetReg(4); + ctx->reg[5] = Core::g_app_core->GetReg(5); + ctx->reg[6] = Core::g_app_core->GetReg(6); + ctx->reg[7] = Core::g_app_core->GetReg(7); + ctx->reg[8] = Core::g_app_core->GetReg(8); + ctx->reg[9] = Core::g_app_core->GetReg(9); + ctx->reg[10] = Core::g_app_core->GetReg(10); + ctx->reg[11] = Core::g_app_core->GetReg(11); + ctx->reg[12] = Core::g_app_core->GetReg(12); + ctx->reg[13] = Core::g_app_core->GetReg(13); + ctx->reg[14] = Core::g_app_core->GetReg(14); + ctx->reg[15] = Core::g_app_core->GetReg(15); + ctx->pc = Core::g_app_core->GetPC(); + ctx->cpsr = Core::g_app_core->GetCPSR(); +} + +// Loads a CPU context +void __KernelLoadContext(ThreadContext *ctx) { + Core::g_app_core->SetReg(0, ctx->reg[0]); + Core::g_app_core->SetReg(1, ctx->reg[1]); + Core::g_app_core->SetReg(2, ctx->reg[2]); + Core::g_app_core->SetReg(3, ctx->reg[3]); + Core::g_app_core->SetReg(4, ctx->reg[4]); + Core::g_app_core->SetReg(5, ctx->reg[5]); + Core::g_app_core->SetReg(6, ctx->reg[6]); + Core::g_app_core->SetReg(7, ctx->reg[7]); + Core::g_app_core->SetReg(8, ctx->reg[8]); + Core::g_app_core->SetReg(9, ctx->reg[9]); + Core::g_app_core->SetReg(10, ctx->reg[10]); + Core::g_app_core->SetReg(11, ctx->reg[11]); + Core::g_app_core->SetReg(12, ctx->reg[12]); + Core::g_app_core->SetReg(13, ctx->reg[13]); + Core::g_app_core->SetReg(14, ctx->reg[14]); + Core::g_app_core->SetReg(15, ctx->reg[15]); + Core::g_app_core->SetPC(ctx->pc); + Core::g_app_core->SetCPSR(ctx->cpsr); +} + +void __KernelSwitchContext(Thread *target, const char *reason) { + u32 oldPC = 0; + UID oldUID = 0; + const char *oldName = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; + + Thread *cur = __GetCurrentThread(); + if (cur) { // It might just have been deleted. + __KernelSaveContext(&cur->context); + oldPC = Core::g_app_core->GetPC(); + oldUID = cur->GetUID(); + + // Normally this is taken care of in __KernelNextThread(). + if (cur->IsRunning()) + __KernelChangeReadyState(cur, oldUID, true); + } + + if (target) { + __SetCurrentThread(target, target->GetUID(), target->nt.name); + __KernelChangeReadyState(target, g_current_thread, false); + target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; + + __KernelLoadContext(&target->context); + } else { + __SetCurrentThread(NULL, 0, NULL); + } + +#if DEBUG_LEVEL <= MAX_LOGLEVEL || DEBUG_LOG == NOTICE_LOG + //bool fromIdle = oldUID == threadIdleID[0] || oldUID == threadIdleID[1]; + //bool toIdle = currentThread == threadIdleID[0] || currentThread == threadIdleID[1]; + //if (!(fromIdle && toIdle)) + //{ + // u64 nowCycles = CoreTiming::GetTicks(); + // s64 consumedCycles = nowCycles - lastSwitchCycles; + // lastSwitchCycles = nowCycles; + + // DEBUG_LOG(SCEKERNEL, "Context switch: %s -> %s (%i->%i, pc: %08x->%08x, %s) +%lldus", + // oldName, hleCurrentThreadName, + // oldUID, currentThread, + // oldPC, currentMIPS->pc, + // reason, + // cyclesToUs(consumedCycles)); + //} +#endif + + if (target) { + //// No longer waiting. + //target->nt.waitType = WAITTYPE_NONE; + //target->nt.waitID = 0; + + //__KernelExecutePendingARMCalls(target, true); + } +} + +UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { + UID id; + + Thread *thread = __KernelCreateThread(id, module_id, "root", prio, Core::g_app_core->GetPC(), + arg, Memory::SCRATCHPAD_VADDR_END, 0xFFFFFFFE, stack_size=stack_size); + + if (thread->current_stack.start == 0) { + ERROR_LOG(KERNEL, "Unable to allocate stack for root thread."); + } + __KernelResetThread(thread, 0); + + Thread *prev_thread = __GetCurrentThread(); + if (prev_thread && prev_thread->IsRunning()) + __KernelChangeReadyState(g_current_thread, true); + __SetCurrentThread(thread, id, "root"); + thread->nt.status = THREADSTATUS_RUNNING; // do not schedule + + strcpy(thread->nt.name, "root"); + + __KernelLoadContext(&thread->context); + + // NOTE(bunnei): Not sure this is really correct, ignore args for now... + //Core::g_app_core->SetReg(0, args); + //Core::g_app_core->SetReg(13, (args + 0xf) & ~0xf); // Setup SP - probably not correct + //u32 location = Core::g_app_core->GetReg(13); // SP + //Core::g_app_core->SetReg(1, location); + + //if (argp) + // Memory::Memcpy(location, argp, args); + //// Let's assume same as starting a new thread, 64 bytes for safety/kernel. + //Core::g_app_core->SetReg(13, Core::g_app_core->GetReg(13) - 64); + + return id; +} + void __KernelThreadingInit() { } void __KernelThreadingShutdown() { } - -//const char *__KernelGetThreadName(UID threadID); -// -//void __KernelSaveContext(ThreadContext *ctx); -//void __KernelLoadContext(ThreadContext *ctx); - -//void __KernelSwitchContext(Thread *target, const char *reason); \ No newline at end of file diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c3cdca31f4..38180cb9bf 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -7,12 +7,12 @@ #include "common/common_types.h" enum ThreadStatus { - THREADSTATUS_RUNNING = 1, - THREADSTATUS_READY = 2, - THREADSTATUS_WAIT = 4, - THREADSTATUS_SUSPEND = 8, - THREADSTATUS_DORMANT = 16, - THREADSTATUS_DEAD = 32, + THREADSTATUS_RUNNING = 1, + THREADSTATUS_READY = 2, + THREADSTATUS_WAIT = 4, + THREADSTATUS_SUSPEND = 8, + THREADSTATUS_DORMANT = 16, + THREADSTATUS_DEAD = 32, THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND }; @@ -25,6 +25,19 @@ struct ThreadContext { u32 pc; }; +class Thread; + +Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, u32 entrypoint, + u32 arg, u32 stack_top, u32 processor_id, int stack_size=0x4000); +void __KernelResetThread(Thread *t, int lowest_priority); +void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready); +void __KernelChangeReadyState(UID thread_id, bool ready); +Thread* __KernelNextThread(); +void __KernelSaveContext(ThreadContext *ctx); +void __KernelLoadContext(ThreadContext *ctx); +void __KernelSwitchContext(Thread *target, const char *reason); +UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size=0x4000); + void __KernelThreadingInit(); void __KernelThreadingShutdown(); From 7d078189daec2db8a465a401b6867739fea5043d Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 22:29:31 -0400 Subject: [PATCH 11/72] various cleanups / remove unused code --- src/core/hle/kernel/thread.cpp | 93 ++++++++++------------------------ src/core/hle/kernel/thread.h | 1 + 2 files changed, 29 insertions(+), 65 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 584276eecb..95ef2c173d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -427,10 +427,11 @@ std::vector g_thread_queue; // Lists only ready thread ids ThreadQueueList g_thread_ready_queue; -UID g_current_thread; -Thread* g_current_thread_ptr; -const char *g_hle_current_thread_name = NULL; +UID g_current_thread = 0; +Thread* g_current_thread_ptr = NULL; +const char* g_hle_current_thread_name = NULL; +/// Creates a new thread Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { @@ -459,38 +460,25 @@ Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 prior return t; } +/// Resets the specified thread back to initial calling state void __KernelResetThread(Thread *t, int lowest_priority) { t->context.reset(); t->context.pc = t->nt.entry_point; // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... - if (t->nt.current_priority < lowest_priority) + if (t->nt.current_priority < lowest_priority) { t->nt.current_priority = t->nt.initial_priority; + } - //t->nt.wait_type = WAITTYPE_NONE; - //t->nt.wait_id = 0; memset(&t->waitInfo, 0, sizeof(t->waitInfo)); - - //t->nt.exitStatus = SCE_KERNEL_ERROR_NOT_DORMANT; - //t->isProcessingCallbacks = false; - //t->currentCallbackId = 0; - //t->currentMipscallId = 0; - //t->pendingMipsCalls.clear(); - - //t->context.r[MIPS_REG_RA] = threadReturnHackAddr; //hack! TODO fix - // TODO: Not sure if it's reset here, but this makes sense. - //t->context.r[MIPS_REG_GP] = t->nt.gpreg; - //t->FillStack(); - - //if (!t->waitingThreads.empty()) - // ERROR_LOG(KERNEL, "Resetting thread with threads waiting on end?"); } - +/// Returns the current executing thread inline Thread *__GetCurrentThread() { return g_current_thread_ptr; } +/// Sets the current executing thread inline void __SetCurrentThread(Thread *thread, UID thread_id, const char *name) { g_current_thread = thread_id; g_current_thread_ptr = thread; @@ -526,30 +514,29 @@ void __KernelChangeReadyState(UID thread_id, bool ready) { } } -// Returns NULL if the current thread is fine. +/// Returns NULL if the current thread is fine. Thread* __KernelNextThread() { - UID bestThread; + UID best_thread; // If the current thread is running, it's a valid candidate. Thread *cur = __GetCurrentThread(); if (cur && cur->IsRunning()) { - bestThread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); - if (bestThread != 0) { + best_thread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); + if (best_thread != 0) { __KernelChangeReadyState(cur, g_current_thread, true); } } else { - bestThread = g_thread_ready_queue.pop_first(); + best_thread = g_thread_ready_queue.pop_first(); } - // Assume g_thread_ready_queue has not become corrupt. - if (bestThread != 0) { - return g_kernel_objects.GetFast(bestThread); + if (best_thread != 0) { + return g_kernel_objects.GetFast(best_thread); } else { return NULL; } } -// Saves the current CPU context +/// Saves the current CPU context void __KernelSaveContext(ThreadContext *ctx) { ctx->reg[0] = Core::g_app_core->GetReg(0); ctx->reg[1] = Core::g_app_core->GetReg(1); @@ -571,7 +558,7 @@ void __KernelSaveContext(ThreadContext *ctx) { ctx->cpsr = Core::g_app_core->GetCPSR(); } -// Loads a CPU context +/// Loads a CPU context void __KernelLoadContext(ThreadContext *ctx) { Core::g_app_core->SetReg(0, ctx->reg[0]); Core::g_app_core->SetReg(1, ctx->reg[1]); @@ -593,59 +580,35 @@ void __KernelLoadContext(ThreadContext *ctx) { Core::g_app_core->SetCPSR(ctx->cpsr); } +/// Switches thread context void __KernelSwitchContext(Thread *target, const char *reason) { - u32 oldPC = 0; - UID oldUID = 0; - const char *oldName = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; - + u32 old_pc = 0; + UID old_uid = 0; + const char *old_name = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; Thread *cur = __GetCurrentThread(); + if (cur) { // It might just have been deleted. __KernelSaveContext(&cur->context); - oldPC = Core::g_app_core->GetPC(); - oldUID = cur->GetUID(); + old_pc = Core::g_app_core->GetPC(); + old_uid = cur->GetUID(); // Normally this is taken care of in __KernelNextThread(). if (cur->IsRunning()) - __KernelChangeReadyState(cur, oldUID, true); + __KernelChangeReadyState(cur, old_uid, true); } - if (target) { __SetCurrentThread(target, target->GetUID(), target->nt.name); __KernelChangeReadyState(target, g_current_thread, false); + target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; __KernelLoadContext(&target->context); } else { __SetCurrentThread(NULL, 0, NULL); } - -#if DEBUG_LEVEL <= MAX_LOGLEVEL || DEBUG_LOG == NOTICE_LOG - //bool fromIdle = oldUID == threadIdleID[0] || oldUID == threadIdleID[1]; - //bool toIdle = currentThread == threadIdleID[0] || currentThread == threadIdleID[1]; - //if (!(fromIdle && toIdle)) - //{ - // u64 nowCycles = CoreTiming::GetTicks(); - // s64 consumedCycles = nowCycles - lastSwitchCycles; - // lastSwitchCycles = nowCycles; - - // DEBUG_LOG(SCEKERNEL, "Context switch: %s -> %s (%i->%i, pc: %08x->%08x, %s) +%lldus", - // oldName, hleCurrentThreadName, - // oldUID, currentThread, - // oldPC, currentMIPS->pc, - // reason, - // cyclesToUs(consumedCycles)); - //} -#endif - - if (target) { - //// No longer waiting. - //target->nt.waitType = WAITTYPE_NONE; - //target->nt.waitID = 0; - - //__KernelExecutePendingARMCalls(target, true); - } } +/// Sets up the root (primary) thread of execution UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { UID id; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 38180cb9bf..1731248cc2 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -5,6 +5,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/kernel/kernel.h" enum ThreadStatus { THREADSTATUS_RUNNING = 1, From 0de78eb3c4407cfedb8779422d96b7ee73fc19ed Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 13 May 2014 23:18:28 -0400 Subject: [PATCH 12/72] fixed thread reset to not set stack address --- src/core/hle/kernel/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 95ef2c173d..c59d2a6893 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -417,7 +417,6 @@ void ThreadContext::reset() { for (int i = 0; i < 16; i++) { reg[i] = 0; } - reg[13] = Memory::SCRATCHPAD_VADDR_END; cpsr = 0; } @@ -464,6 +463,7 @@ Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 prior void __KernelResetThread(Thread *t, int lowest_priority) { t->context.reset(); t->context.pc = t->nt.entry_point; + t->context.reg[13] = t->nt.initial_stack; // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... if (t->nt.current_priority < lowest_priority) { From 4d7f3c74fd8bc86bf73b670355e2710558a66332 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 May 2014 20:49:02 -0400 Subject: [PATCH 13/72] added function stubs for EatCycles and ReSchedule --- src/core/hle/hle.cpp | 11 +++++++++++ src/core/hle/hle.h | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index be151665b2..4523845711 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -37,6 +37,17 @@ void CallSyscall(u32 opcode) { } } +void EatCycles(u32 cycles) { + // TODO: ImplementMe +} + +void ReSchedule(const char *reason) { +#ifdef _DEBUG + _dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "ReSchedule: Invalid or too long reason."); +#endif + // TODO: ImplementMe +} + void RegisterModule(std::string name, int num_functions, const FunctionDef* func_table) { ModuleDef module = {name, num_functions, func_table}; g_module_db.push_back(module); diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h index 42f37e29cf..452546e1f8 100644 --- a/src/core/hle/hle.h +++ b/src/core/hle/hle.h @@ -36,6 +36,10 @@ void RegisterModule(std::string name, int num_functions, const FunctionDef *func void CallSyscall(u32 opcode); +void EatCycles(u32 cycles); + +void ReSchedule(const char *reason); + void Init(); void Shutdown(); From 9f5588725c9c16543f55ad05965086b8962a460f Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 May 2014 20:49:27 -0400 Subject: [PATCH 14/72] changed primary thread priority to 0x30 - this is typical, not 0x31 --- src/core/hle/kernel/kernel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index fb6ae8495a..1c26fb388e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -147,7 +147,8 @@ bool __KernelLoadExec(u32 entry_point) { Core::g_app_core->SetPC(entry_point); - UID thread_id = __KernelSetupRootThread(0xDEADBEEF, 0, 0x31); + // 0x30 is the typical main thread priority I've seen used so far + UID thread_id = __KernelSetupRootThread(0xDEADBEEF, 0, 0x30); return true; } From b99a5da65b76ce16e40fe05feb786aac11931904 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 14 May 2014 20:50:30 -0400 Subject: [PATCH 15/72] - added helper function for __KernelCreateThread - added __KernelSwitchToThread for enabling a thread - added __KernelRotateThreadReadyQueue --- src/core/hle/kernel/thread.cpp | 74 ++++++++++++++++++++++++++++++++-- src/core/hle/kernel/thread.h | 6 +++ 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index c59d2a6893..b6d02aa120 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -13,6 +13,8 @@ #include "core/core.h" #include "core/mem_map.h" +#include "core/hle/hle.h" +#include "core/hle/syscall.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" @@ -357,7 +359,7 @@ public: //void Cleanup() { // // Callbacks are automatically deleted when their owning thread is deleted. // for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) - // kernelObjects.Destroy(*it); + // g_kernel_objects.Destroy(*it); // if (pushed_stacks.size() != 0) // { @@ -432,7 +434,7 @@ const char* g_hle_current_thread_name = NULL; /// Creates a new thread Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, - u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { + u32 entry_point, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { Thread *t = new Thread; id = g_kernel_objects.Create(t); @@ -442,7 +444,7 @@ Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 prior memset(&t->nt, 0xCD, sizeof(t->nt)); - t->nt.entry_point = entrypoint; + t->nt.entry_point = entry_point; t->nt.native_size = sizeof(t->nt); t->nt.initial_priority = t->nt.current_priority = priority; t->nt.status = THREADSTATUS_DORMANT; @@ -459,6 +461,18 @@ Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 prior return t; } +UID __KernelCreateThread(UID module_id, const char* name, u32 priority, u32 entry_point, u32 arg, + u32 stack_top, u32 processor_id, int stack_size) { + UID id; + __KernelCreateThread(id, module_id, name, priority, entry_point, arg, stack_top, processor_id, + stack_size); + + HLE::EatCycles(32000); + HLE::ReSchedule("thread created"); + + return id; +} + /// Resets the specified thread back to initial calling state void __KernelResetThread(Thread *t, int lowest_priority) { t->context.reset(); @@ -608,6 +622,31 @@ void __KernelSwitchContext(Thread *target, const char *reason) { } } +bool __KernelSwitchToThread(UID thread_id, const char *reason) { + if (!reason) { + reason = "switch to thread"; + } + if (g_current_thread == thread_id) { + return false; + } + u32 error; + Thread *t = g_kernel_objects.Get(thread_id, error); + if (!t) { + ERROR_LOG(KERNEL, "__KernelSwitchToThread: %x doesn't exist", thread_id); + HLE::ReSchedule("switch to deleted thread"); + } else if (t->IsReady() || t->IsRunning()) { + Thread *current = __GetCurrentThread(); + if (current && current->IsRunning()) { + __KernelChangeReadyState(current, g_current_thread, true); + } + __KernelSwitchContext(t, reason); + return true; + } else { + HLE::ReSchedule("switch to waiting thread"); + } + return false; +} + /// Sets up the root (primary) thread of execution UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { UID id; @@ -633,7 +672,7 @@ UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { // NOTE(bunnei): Not sure this is really correct, ignore args for now... //Core::g_app_core->SetReg(0, args); //Core::g_app_core->SetReg(13, (args + 0xf) & ~0xf); // Setup SP - probably not correct - //u32 location = Core::g_app_core->GetReg(13); // SP + //u32 location = Core::g_app_core->GetReg(13); // SP //Core::g_app_core->SetReg(1, location); //if (argp) @@ -644,6 +683,33 @@ UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { return id; } +int __KernelRotateThreadReadyQueue(int priority) { + Thread *cur = __GetCurrentThread(); + + // 0 is special, it means "my current priority." + if (priority == 0) { + priority = cur->nt.current_priority; + } + //if (priority <= 0x07 || priority > 0x77) + // return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY; + + if (!g_thread_ready_queue.empty(priority)) { + // In other words, yield to everyone else. + if (cur->nt.current_priority == priority) { + g_thread_ready_queue.push_back(priority, g_current_thread); + cur->nt.status = (cur->nt.status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; + + // Yield the next thread of this priority to all other threads of same priority. + } else { + g_thread_ready_queue.rotate(priority); + } + } + HLE::EatCycles(250); + HLE::ReSchedule("rotatethreadreadyqueue"); + + return 0; +} + void __KernelThreadingInit() { } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 1731248cc2..05468fb2e0 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -30,6 +30,10 @@ class Thread; Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, u32 entrypoint, u32 arg, u32 stack_top, u32 processor_id, int stack_size=0x4000); + +UID __KernelCreateThread(UID module_id, const char* name, u32 priority, u32 entry_point, u32 arg, + u32 stack_top, u32 processor_id, int stack_size=0x4000); + void __KernelResetThread(Thread *t, int lowest_priority); void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready); void __KernelChangeReadyState(UID thread_id, bool ready); @@ -37,7 +41,9 @@ Thread* __KernelNextThread(); void __KernelSaveContext(ThreadContext *ctx); void __KernelLoadContext(ThreadContext *ctx); void __KernelSwitchContext(Thread *target, const char *reason); +bool __KernelSwitchToThread(UID thread_id, const char *reason); UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size=0x4000); +int __KernelRotateThreadReadyQueue(int priority=0); void __KernelThreadingInit(); void __KernelThreadingShutdown(); From cf2eb8e3d3876d5866bdcb5dfe2ae9deceea2cb4 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 May 2014 18:19:34 -0400 Subject: [PATCH 16/72] added ThreadQueueList class to common (taken from PPSSPP) --- src/common/common.vcxproj | 1 + src/common/common.vcxproj.filters | 1 + src/common/thread_queue_list.h | 216 ++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 src/common/thread_queue_list.h diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index 5dc6ff7907..86295a4801 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -190,6 +190,7 @@ + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 268730228d..84cfa8837f 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -40,6 +40,7 @@ + diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h new file mode 100644 index 0000000000..4a89572f60 --- /dev/null +++ b/src/common/thread_queue_list.h @@ -0,0 +1,216 @@ +// Copyright 2014 Citra Emulator Project / PPSSPP Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "common/common.h" + +namespace Common { + +template +struct ThreadQueueList { + // Number of queues (number of priority levels starting at 0.) + static const int NUM_QUEUES = 128; + + // Initial number of threads a single queue can handle. + static const int INITIAL_CAPACITY = 32; + + struct Queue { + // Next ever-been-used queue (worse priority.) + Queue *next; + // First valid item in data. + int first; + // One after last valid item in data. + int end; + // A too-large array with room on the front and end. + IdType *data; + // Size of data array. + int capacity; + }; + + ThreadQueueList() { + memset(queues, 0, sizeof(queues)); + first = invalid(); + } + + ~ThreadQueueList() { + for (int i = 0; i < NUM_QUEUES; ++i) + { + if (queues[i].data != NULL) + free(queues[i].data); + } + } + + // Only for debugging, returns priority level. + int contains(const IdType uid) { + for (int i = 0; i < NUM_QUEUES; ++i) + { + if (queues[i].data == NULL) + continue; + + Queue *cur = &queues[i]; + for (int j = cur->first; j < cur->end; ++j) + { + if (cur->data[j] == uid) + return i; + } + } + + return -1; + } + + inline IdType pop_first() { + Queue *cur = first; + while (cur != invalid()) + { + if (cur->end - cur->first > 0) + return cur->data[cur->first++]; + cur = cur->next; + } + + //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty."); + return 0; + } + + inline IdType pop_first_better(u32 priority) { + Queue *cur = first; + Queue *stop = &queues[priority]; + while (cur < stop) + { + if (cur->end - cur->first > 0) + return cur->data[cur->first++]; + cur = cur->next; + } + + return 0; + } + + inline void push_front(u32 priority, const IdType threadID) { + Queue *cur = &queues[priority]; + cur->data[--cur->first] = threadID; + if (cur->first == 0) + rebalance(priority); + } + + inline void push_back(u32 priority, const IdType threadID) { + Queue *cur = &queues[priority]; + cur->data[cur->end++] = threadID; + if (cur->end == cur->capacity) + rebalance(priority); + } + + inline void remove(u32 priority, const IdType threadID) { + Queue *cur = &queues[priority]; + //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); + + for (int i = cur->first; i < cur->end; ++i) + { + if (cur->data[i] == threadID) + { + int remaining = --cur->end - i; + if (remaining > 0) + memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType)); + return; + } + } + + // Wasn't there. + } + + inline void rotate(u32 priority) { + Queue *cur = &queues[priority]; + //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); + + if (cur->end - cur->first > 1) + { + cur->data[cur->end++] = cur->data[cur->first++]; + if (cur->end == cur->capacity) + rebalance(priority); + } + } + + inline void clear() { + for (int i = 0; i < NUM_QUEUES; ++i) + { + if (queues[i].data != NULL) + free(queues[i].data); + } + memset(queues, 0, sizeof(queues)); + first = invalid(); + } + + inline bool empty(u32 priority) const { + const Queue *cur = &queues[priority]; + return cur->first == cur->end; + } + + inline void prepare(u32 priority) { + Queue *cur = &queues[priority]; + if (cur->next == NULL) + link(priority, INITIAL_CAPACITY); + } + +private: + Queue *invalid() const { + return (Queue *) -1; + } + + void link(u32 priority, int size) { + //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once."); + + if (size <= INITIAL_CAPACITY) + size = INITIAL_CAPACITY; + else + { + int goal = size; + size = INITIAL_CAPACITY; + while (size < goal) + size *= 2; + } + Queue *cur = &queues[priority]; + cur->data = (IdType *) malloc(sizeof(IdType) * size); + cur->capacity = size; + cur->first = size / 2; + cur->end = size / 2; + + for (int i = (int) priority - 1; i >= 0; --i) + { + if (queues[i].next != NULL) + { + cur->next = queues[i].next; + queues[i].next = cur; + return; + } + } + + cur->next = first; + first = cur; + } + + void rebalance(u32 priority) { + Queue *cur = &queues[priority]; + int size = cur->end - cur->first; + if (size >= cur->capacity - 2) { + IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType)); + if (new_data != NULL) { + cur->capacity *= 2; + cur->data = new_data; + } + } + + int newFirst = (cur->capacity - size) / 2; + if (newFirst != cur->first) { + memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType)); + cur->first = newFirst; + cur->end = newFirst + size; + } + } + + // The first queue that's ever been used. + Queue *first; + // The priority level queues of thread ids. + Queue queues[NUM_QUEUES]; +}; + +} // namespace From 367d63691fe810c83979fee04b181338f96cfb50 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 May 2014 18:25:56 -0400 Subject: [PATCH 17/72] - added ThreadContext struct - cleaned up CreateThread svc --- src/core/hle/syscall.cpp | 20 ++++++++++++-------- src/core/hle/syscall.h | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index c697bc277c..0700d9e827 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -9,6 +9,7 @@ #include "core/hle/function_wrappers.h" #include "core/hle/syscall.h" #include "core/hle/service/service.h" +#include "core/hle/kernel/thread.h" #include "common/symbols.h" @@ -139,16 +140,19 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* return 0; } -Result CreateThread(void* thread, u32 threadpriority, u32 entrypoint, u32 arg, u32 stacktop, u32 processorid) { - std::string symbol_name = "unknown"; - if (Symbols::HasSymbol(entrypoint)) { - TSymbol symbol = Symbols::GetSymbol(entrypoint); - symbol_name = symbol.name; +Result CreateThread(void* thread, u32 thread_priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { + std::string thread_name; + if (Symbols::HasSymbol(entry_point)) { + TSymbol symbol = Symbols::GetSymbol(entry_point); + thread_name = symbol.name; + } else { + char buff[100]; + sprintf(buff, "%s", "unk-%08X", entry_point); + thread_name = buff; } - // stack top: 0x0056A4A0 DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, " - "stacktop=0x%08X, threadpriority=0x%08X, processorid=0x%08X", entrypoint, - symbol_name.c_str(), arg, stacktop, threadpriority, processorid); + "stacktop=0x%08X, threadpriority=0x%08X, processorid=0x%08X", entry_point, + thread_name.c_str(), arg, stack_top, thread_priority, processor_id); return 0; } diff --git a/src/core/hle/syscall.h b/src/core/hle/syscall.h index 7a94e01367..15af5e1384 100644 --- a/src/core/hle/syscall.h +++ b/src/core/hle/syscall.h @@ -6,6 +6,20 @@ #include "common/common_types.h" +//////////////////////////////////////////////////////////////////////////////////////////////////// +// SVC structures + +struct ThreadContext { + u32 cpu_registers[13]; + u32 sp; + u32 lr; + u32 pc; + u32 cpsr; + u32 fpu_registers[32]; + u32 fpscr; + u32 fpexc; +}; + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Syscall From a7cc430aa4da2962dcf08db2f6009fc272bdda70 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 May 2014 18:26:28 -0400 Subject: [PATCH 18/72] changed "UID" to "Handle" to be a little more consistent with CTR naming --- src/core/hle/kernel/kernel.cpp | 10 +++++----- src/core/hle/kernel/kernel.h | 29 ++++++++++++++++------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1c26fb388e..f7145ddd8f 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -27,7 +27,7 @@ KernelObjectPool::KernelObjectPool() { next_id = INITIAL_NEXT_ID; } -UID KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_top) { +Handle KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_top) { if (range_top > MAX_COUNT) { range_top = MAX_COUNT; } @@ -38,7 +38,7 @@ UID KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_top) if (!occupied[i]) { occupied[i] = true; pool[i] = obj; - pool[i]->uid = i + HANDLE_OFFSET; + pool[i]->handle = i + HANDLE_OFFSET; return i + HANDLE_OFFSET; } } @@ -46,7 +46,7 @@ UID KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_top) return 0; } -bool KernelObjectPool::IsValid(UID handle) +bool KernelObjectPool::IsValid(Handle handle) { int index = handle - HANDLE_OFFSET; if (index < 0) @@ -70,7 +70,7 @@ void KernelObjectPool::Clear() next_id = INITIAL_NEXT_ID; } -KernelObject *&KernelObjectPool::operator [](UID handle) +KernelObject *&KernelObjectPool::operator [](Handle handle) { _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); return pool[handle - HANDLE_OFFSET]; @@ -148,7 +148,7 @@ bool __KernelLoadExec(u32 entry_point) { Core::g_app_core->SetPC(entry_point); // 0x30 is the typical main thread priority I've seen used so far - UID thread_id = __KernelSetupRootThread(0xDEADBEEF, 0, 0x30); + Handle thread_id = __KernelSetupMainThread(0x30); return true; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 0eb58210cc..24d422682e 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -6,7 +6,7 @@ #include "common/common_types.h" -typedef u32 UID; +typedef s32 Handle; enum KernelIDType { KERNEL_ID_TYPE_THREAD = 1, @@ -15,20 +15,23 @@ enum KernelIDType { KERNEL_ID_TYPE_EVENT = 4, }; +enum { + KERNELOBJECT_MAX_NAME_LENGTH = 255, +}; + #define KERNELOBJECT_MAX_NAME_LENGTH 31 class KernelObjectPool; class KernelObject { friend class KernelObjectPool; - u32 uid; + u32 handle; public: virtual ~KernelObject() {} - UID GetUID() const { return uid; } + Handle GetHandle() const { return handle; } virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; } virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; } virtual KernelIDType GetIDType() const = 0; - //virtual void GetQuickInfo(char *ptr, int size); }; class KernelObjectPool { @@ -36,13 +39,13 @@ public: KernelObjectPool(); ~KernelObjectPool() {} - // Allocates a UID within the range and inserts the object into the map. - UID Create(KernelObject *obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); + // Allocates a handle within the range and inserts the object into the map. + Handle Create(KernelObject *obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); static KernelObject *CreateByIDType(int type); template - u32 Destroy(UID handle) { + u32 Destroy(Handle handle) { u32 error; if (Get(handle, error)) { occupied[handle - HANDLE_OFFSET] = false; @@ -51,10 +54,10 @@ public: return error; }; - bool IsValid(UID handle); + bool IsValid(Handle handle); template - T* Get(UID handle, u32& outError) { + T* Get(Handle handle, u32& outError) { if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP if (handle != 0 && (u32)handle != 0x80020001) { @@ -79,8 +82,8 @@ public: // ONLY use this when you know the handle is valid. template - T *GetFast(UID handle) { - const UID realHandle = handle - HANDLE_OFFSET; + T *GetFast(Handle handle) { + const Handle realHandle = handle - HANDLE_OFFSET; _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); return static_cast(pool[realHandle]); } @@ -100,7 +103,7 @@ public: } } - bool GetIDType(UID handle, int *type) const { + bool GetIDType(Handle handle, int *type) const { if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || !occupied[handle - HANDLE_OFFSET]) { ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); @@ -111,7 +114,7 @@ public: return true; } - KernelObject *&operator [](UID handle); + KernelObject *&operator [](Handle handle); void List(); void Clear(); int GetCount(); From 940330c6e12b3eefb9fb035f75f4b090c969cb75 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 May 2014 18:27:08 -0400 Subject: [PATCH 19/72] completely gutted/refactored threading code to be simpler --- src/core/hle/kernel/thread.cpp | 894 ++++++++++----------------------- src/core/hle/kernel/thread.h | 44 +- 2 files changed, 255 insertions(+), 683 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b6d02aa120..833a1b4bad 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -10,6 +10,7 @@ #include #include "common/common.h" +#include "common/thread_queue_list.h" #include "core/core.h" #include "core/mem_map.h" @@ -18,698 +19,309 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -struct ThreadQueueList { - // Number of queues (number of priority levels starting at 0.) - static const int NUM_QUEUES = 128; - // Initial number of threads a single queue can handle. - static const int INITIAL_CAPACITY = 32; +// Enums - struct Queue { - // Next ever-been-used queue (worse priority.) - Queue *next; - // First valid item in data. - int first; - // One after last valid item in data. - int end; - // A too-large array with room on the front and end. - UID *data; - // Size of data array. - int capacity; - }; - - ThreadQueueList() { - memset(queues, 0, sizeof(queues)); - first = invalid(); - } - - ~ThreadQueueList() { - for (int i = 0; i < NUM_QUEUES; ++i) { - if (queues[i].data != NULL) { - free(queues[i].data); - } - } - } - - // Only for debugging, returns priority level. - int contains(const UID uid) { - for (int i = 0; i < NUM_QUEUES; ++i) { - if (queues[i].data == NULL) { - continue; - } - Queue *cur = &queues[i]; - for (int j = cur->first; j < cur->end; ++j) { - if (cur->data[j] == uid) { - return i; - } - } - } - return -1; - } - - inline UID pop_first() { - Queue *cur = first; - while (cur != invalid()) { - if (cur->end - cur->first > 0) { - return cur->data[cur->first++]; - } - cur = cur->next; - } - - _dbg_assert_msg_(KERNEL, false, "ThreadQueueList should not be empty."); - return 0; - } - - inline UID pop_first_better(u32 priority) { - Queue *cur = first; - Queue *stop = &queues[priority]; - while (cur < stop) { - if (cur->end - cur->first > 0) { - return cur->data[cur->first++]; - } - cur = cur->next; - } - return 0; - } - - inline void push_front(u32 priority, const UID thread_id) { - Queue *cur = &queues[priority]; - cur->data[--cur->first] = thread_id; - if (cur->first == 0) { - rebalance(priority); - } - } - - inline void push_back(u32 priority, const UID thread_id) - { - Queue *cur = &queues[priority]; - cur->data[cur->end++] = thread_id; - if (cur->end == cur->capacity) { - rebalance(priority); - } - } - - inline void remove(u32 priority, const UID thread_id) { - Queue *cur = &queues[priority]; - _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); - - for (int i = cur->first; i < cur->end; ++i) { - if (cur->data[i] == thread_id) { - int remaining = --cur->end - i; - if (remaining > 0) { - memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(UID)); - } - return; - } - } - - // Wasn't there. - } - - inline void rotate(u32 priority) { - Queue *cur = &queues[priority]; - _dbg_assert_msg_(KERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); - - if (cur->end - cur->first > 1) { - cur->data[cur->end++] = cur->data[cur->first++]; - if (cur->end == cur->capacity) { - rebalance(priority); - } - } - } - - inline void clear() { - for (int i = 0; i < NUM_QUEUES; ++i) { - if (queues[i].data != NULL) { - free(queues[i].data); - } - } - memset(queues, 0, sizeof(queues)); - first = invalid(); - } - - inline bool empty(u32 priority) const { - const Queue *cur = &queues[priority]; - return cur->first == cur->end; - } - - inline void prepare(u32 priority) { - Queue *cur = &queues[priority]; - if (cur->next == NULL) { - link(priority, INITIAL_CAPACITY); - } - } - -private: - Queue *invalid() const { - return (Queue *)-1; - } - - void link(u32 priority, int size) { - _dbg_assert_msg_(KERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once."); - - if (size <= INITIAL_CAPACITY) { - size = INITIAL_CAPACITY; - } else { - int goal = size; - size = INITIAL_CAPACITY; - while (size < goal) - size *= 2; - } - Queue *cur = &queues[priority]; - cur->data = (UID*)malloc(sizeof(UID)* size); - cur->capacity = size; - cur->first = size / 2; - cur->end = size / 2; - - for (int i = (int)priority - 1; i >= 0; --i) { - if (queues[i].next != NULL) { - cur->next = queues[i].next; - queues[i].next = cur; - return; - } - } - - cur->next = first; - first = cur; - } - - void rebalance(u32 priority) { - Queue *cur = &queues[priority]; - int size = cur->end - cur->first; - if (size >= cur->capacity - 2) { - UID* new_data = (UID*)realloc(cur->data, cur->capacity * 2 * sizeof(UID)); - if (new_data != NULL) { - cur->capacity *= 2; - cur->data = new_data; - } - } - - int newFirst = (cur->capacity - size) / 2; - if (newFirst != cur->first) { - memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(UID)); - cur->first = newFirst; - cur->end = newFirst + size; - } - } - - // The first queue that's ever been used. - Queue* first; - // The priority level queues of thread ids. - Queue queues[NUM_QUEUES]; +enum ThreadPriority { + THREADPRIO_HIGHEST = 0, + THREADPRIO_DEFAULT = 16, + THREADPRIO_LOWEST = 31, }; -// Supposed to represent a real CTR struct... but not sure of the correct fields yet. -struct NativeThread { - //u32 Pointer to vtable - //u32 Reference count - //KProcess* Process the thread belongs to (virtual address) - //u32 Thread id - //u32* ptr = *(KThread+0x8C) - 0xB0 - //u32* End-address of the page for this thread allocated in the 0xFF4XX000 region. Thus, - // if the beginning of this mapped page is 0xFF401000, this ptr would be 0xFF402000. - //KThread* Previous ? (virtual address) - //KThread* Next ? (virtual address) +enum ThreadStatus { + THREADSTATUS_RUNNING = 1, + THREADSTATUS_READY = 2, + THREADSTATUS_WAIT = 4, + THREADSTATUS_SUSPEND = 8, + THREADSTATUS_DORMANT = 16, + THREADSTATUS_DEAD = 32, + THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND +}; - u32_le native_size; - char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; - - // Threading stuff - u32_le status; - u32_le entry_point; - u32_le initial_stack; - u32_le stack_top; - u32_le stack_size; +enum WaitType { + WAITTYPE_NONE, + WAITTYPE_SLEEP, + WAITTYPE_SEMA, + WAITTYPE_EVENTFLAG, + WAITTYPE_THREADEND, + WAITTYPE_VBLANK, + WAITTYPE_MUTEX, + WAITTYPE_SYNCH, - u32_le arg; - u32_le processor_id; - - s32_le initial_priority; - s32_le current_priority; + NUM_WAITTYPES }; -struct ThreadWaitInfo { - u32 wait_value; - u32 timeout_ptr; -}; +typedef s32 Handle; class Thread : public KernelObject { public: - /*const char *GetName() { return nt.name; }*/ - const char *GetTypeName() { return "Thread"; } - //void GetQuickInfo(char *ptr, int size) - //{ - // sprintf(ptr, "pc= %08x sp= %08x %s %s %s %s %s %s (wt=%i wid=%i wv= %08x )", - // context.pc, context.r[13], // 13 is stack pointer - // (nt.status & THREADSTATUS_RUNNING) ? "RUN" : "", - // (nt.status & THREADSTATUS_READY) ? "READY" : "", - // (nt.status & THREADSTATUS_WAIT) ? "WAIT" : "", - // (nt.status & THREADSTATUS_SUSPEND) ? "SUSPEND" : "", - // (nt.status & THREADSTATUS_DORMANT) ? "DORMANT" : "", - // (nt.status & THREADSTATUS_DEAD) ? "DEAD" : "", - // nt.waitType, - // nt.waitID, - // waitInfo.waitValue); - //} - //static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_THID; } + const char *GetName() { return name; } + const char *GetTypeName() { return "Thread"; } + static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } - bool SetupStack(u32 stack_top, int stack_size) { - current_stack.start = stack_top; - nt.initial_stack = current_stack.start; - nt.stack_size = stack_size; - return true; - } - - //bool FillStack() { - // // Fill the stack. - // if ((nt.attr & PSP_THREAD_ATTR_NO_FILLSTACK) == 0) { - // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); - // } - // context.r[MIPS_REG_SP] = current_stack.start + nt.stack_size; - // current_stack.end = context.r[MIPS_REG_SP]; - // // The k0 section is 256 bytes at the top of the stack. - // context.r[MIPS_REG_SP] -= 256; - // context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP]; - // u32 k0 = context.r[MIPS_REG_K0]; - // Memory::Memset(k0, 0, 0x100); - // Memory::Write_U32(GetUID(), k0 + 0xc0); - // Memory::Write_U32(nt.initialStack, k0 + 0xc8); - // Memory::Write_U32(0xffffffff, k0 + 0xf8); - // Memory::Write_U32(0xffffffff, k0 + 0xfc); - // // After k0 comes the arguments, which is done by sceKernelStartThread(). - - // Memory::Write_U32(GetUID(), nt.initialStack); - // return true; - //} - - //void FreeStack() { - // if (current_stack.start != 0) { - // DEBUG_LOG(KERNEL, "Freeing thread stack %s", nt.name); - - // if ((nt.attr & PSP_THREAD_ATTR_CLEAR_STACK) != 0 && nt.initialStack != 0) { - // Memory::Memset(nt.initialStack, 0, nt.stack_size); - // } - - // if (nt.attr & PSP_THREAD_ATTR_KERNEL) { - // kernelMemory.Free(current_stack.start); - // } - // else { - // userMemory.Free(current_stack.start); - // } - // current_stack.start = 0; - // } - //} - - //bool PushExtendedStack(u32 size) { - // u32 stack = userMemory.Alloc(size, true, (std::string("extended/") + nt.name).c_str()); - // if (stack == (u32)-1) - // return false; - - // pushed_stacks.push_back(current_stack); - // current_stack.start = stack; - // current_stack.end = stack + size; - // nt.initialStack = current_stack.start; - // nt.stack_size = current_stack.end - current_stack.start; - - // // We still drop the thread_id at the bottom and fill it, but there's no k0. - // Memory::Memset(current_stack.start, 0xFF, nt.stack_size); - // Memory::Write_U32(GetUID(), nt.initialStack); - // return true; - //} - - //bool PopExtendedStack() { - // if (pushed_stacks.size() == 0) { - // return false; - // } - // userMemory.Free(current_stack.start); - // current_stack = pushed_stacks.back(); - // pushed_stacks.pop_back(); - // nt.initialStack = current_stack.start; - // nt.stack_size = current_stack.end - current_stack.start; - // return true; - //} - - Thread() { - current_stack.start = 0; - } - - // Can't use a destructor since savestates will call that too. - //void Cleanup() { - // // Callbacks are automatically deleted when their owning thread is deleted. - // for (auto it = callbacks.begin(), end = callbacks.end(); it != end; ++it) - // g_kernel_objects.Destroy(*it); - - // if (pushed_stacks.size() != 0) - // { - // WARN_LOG(KERNEL, "Thread ended within an extended stack"); - // for (size_t i = 0; i < pushed_stacks.size(); ++i) - // userMemory.Free(pushed_stacks[i].start); - // } - // FreeStack(); - //} - - void setReturnValue(u32 retval); - void setReturnValue(u64 retval); - void resumeFromWait(); - //bool isWaitingFor(WaitType type, int id); - //int getWaitID(WaitType type); - ThreadWaitInfo getWaitInfo(); - - // Utils - inline bool IsRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; } - inline bool IsStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; } - inline bool IsReady() const { return (nt.status & THREADSTATUS_READY) != 0; } - inline bool IsWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; } - inline bool IsSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; } - - NativeThread nt; - - ThreadWaitInfo waitInfo; - UID moduleId; - - //bool isProcessingCallbacks; - //u32 currentMipscallId; - //UID currentCallbackId; + 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; } ThreadContext context; - std::vector callbacks; + u32 status; + u32 entry_point; + u32 stack_top; + u32 stack_size; - std::list pending_calls; + s32 initial_priority; + s32 current_priority; - struct StackInfo { - u32 start; - u32 end; - }; - // This is a stack of... stacks, since sceKernelExtendThreadStack() can recurse. - // These are stacks that aren't "active" right now, but will pop off once the func returns. - std::vector pushed_stacks; + s32 processor_id; - StackInfo current_stack; + WaitType wait_type; - // For thread end. - std::vector waiting_threads; - // Key is the callback id it was for, or if no callback, the thread id. - std::map paused_waits; + char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; }; -void ThreadContext::reset() { - for (int i = 0; i < 16; i++) { - reg[i] = 0; - } - cpsr = 0; -} - // Lists all thread ids that aren't deleted/etc. -std::vector g_thread_queue; +std::vector g_thread_queue; -// Lists only ready thread ids -ThreadQueueList g_thread_ready_queue; +// Lists only ready thread ids. +Common::ThreadQueueList g_thread_ready_queue; -UID g_current_thread = 0; -Thread* g_current_thread_ptr = NULL; -const char* g_hle_current_thread_name = NULL; +Handle g_current_thread_handle; -/// Creates a new thread -Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, - u32 entry_point, u32 arg, u32 stack_top, u32 processor_id, int stack_size) { +Thread* g_current_thread; - Thread *t = new Thread; - id = g_kernel_objects.Create(t); - g_thread_queue.push_back(id); - g_thread_ready_queue.prepare(priority); - - memset(&t->nt, 0xCD, sizeof(t->nt)); - - t->nt.entry_point = entry_point; - t->nt.native_size = sizeof(t->nt); - t->nt.initial_priority = t->nt.current_priority = priority; - t->nt.status = THREADSTATUS_DORMANT; - t->nt.initial_stack = t->nt.stack_top = stack_top; - t->nt.stack_size = stack_size; - t->nt.processor_id = processor_id; - - strncpy(t->nt.name, name, KERNELOBJECT_MAX_NAME_LENGTH); - t->nt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; - - t->nt.stack_size = stack_size; - t->SetupStack(stack_top, stack_size); - - return t; -} - -UID __KernelCreateThread(UID module_id, const char* name, u32 priority, u32 entry_point, u32 arg, - u32 stack_top, u32 processor_id, int stack_size) { - UID id; - __KernelCreateThread(id, module_id, name, priority, entry_point, arg, stack_top, processor_id, - stack_size); - - HLE::EatCycles(32000); - HLE::ReSchedule("thread created"); - - return id; -} - -/// Resets the specified thread back to initial calling state -void __KernelResetThread(Thread *t, int lowest_priority) { - t->context.reset(); - t->context.pc = t->nt.entry_point; - t->context.reg[13] = t->nt.initial_stack; - - // If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd... - if (t->nt.current_priority < lowest_priority) { - t->nt.current_priority = t->nt.initial_priority; - } - - memset(&t->waitInfo, 0, sizeof(t->waitInfo)); -} - -/// Returns the current executing thread inline Thread *__GetCurrentThread() { - return g_current_thread_ptr; + return g_current_thread; } -/// Sets the current executing thread -inline void __SetCurrentThread(Thread *thread, UID thread_id, const char *name) { - g_current_thread = thread_id; - g_current_thread_ptr = thread; - g_hle_current_thread_name = name; +inline void __SetCurrentThread(Thread *t) { + g_current_thread = t; + g_current_thread_handle = t->GetHandle(); } -// TODO: Use __KernelChangeThreadState instead? It has other affects... -void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready) { - // Passing the id as a parameter is just an optimization, if it's wrong it will cause havoc. - _dbg_assert_msg_(KERNEL, thread->GetUID() == thread_id, "Incorrect thread_id"); - int prio = thread->nt.current_priority; - - if (thread->IsReady()) { - if (!ready) - g_thread_ready_queue.remove(prio, thread_id); - } else if (ready) { - if (thread->IsRunning()) { - g_thread_ready_queue.push_front(prio, thread_id); - } else { - g_thread_ready_queue.push_back(prio, thread_id); - } - thread->nt.status = THREADSTATUS_READY; - } -} - -void __KernelChangeReadyState(UID thread_id, bool ready) { - u32 error; - Thread *thread = g_kernel_objects.Get(thread_id, error); - if (thread) { - __KernelChangeReadyState(thread, thread_id, ready); - } else { - WARN_LOG(KERNEL, "Trying to change the ready state of an unknown thread?"); - } -} - -/// Returns NULL if the current thread is fine. -Thread* __KernelNextThread() { - UID best_thread; - - // If the current thread is running, it's a valid candidate. - Thread *cur = __GetCurrentThread(); - if (cur && cur->IsRunning()) { - best_thread = g_thread_ready_queue.pop_first_better(cur->nt.current_priority); - if (best_thread != 0) { - __KernelChangeReadyState(cur, g_current_thread, true); - } - } else { - best_thread = g_thread_ready_queue.pop_first(); - } - // Assume g_thread_ready_queue has not become corrupt. - if (best_thread != 0) { - return g_kernel_objects.GetFast(best_thread); - } else { - return NULL; - } -} +//////////////////////////////////////////////////////////////////////////////////////////////////// /// Saves the current CPU context -void __KernelSaveContext(ThreadContext *ctx) { - ctx->reg[0] = Core::g_app_core->GetReg(0); - ctx->reg[1] = Core::g_app_core->GetReg(1); - ctx->reg[2] = Core::g_app_core->GetReg(2); - ctx->reg[3] = Core::g_app_core->GetReg(3); - ctx->reg[4] = Core::g_app_core->GetReg(4); - ctx->reg[5] = Core::g_app_core->GetReg(5); - ctx->reg[6] = Core::g_app_core->GetReg(6); - ctx->reg[7] = Core::g_app_core->GetReg(7); - ctx->reg[8] = Core::g_app_core->GetReg(8); - ctx->reg[9] = Core::g_app_core->GetReg(9); - ctx->reg[10] = Core::g_app_core->GetReg(10); - ctx->reg[11] = Core::g_app_core->GetReg(11); - ctx->reg[12] = Core::g_app_core->GetReg(12); - ctx->reg[13] = Core::g_app_core->GetReg(13); - ctx->reg[14] = Core::g_app_core->GetReg(14); - ctx->reg[15] = Core::g_app_core->GetReg(15); - ctx->pc = Core::g_app_core->GetPC(); - ctx->cpsr = Core::g_app_core->GetCPSR(); +void __KernelSaveContext(ThreadContext &ctx) { + ctx.cpu_registers[0] = Core::g_app_core->GetReg(0); + ctx.cpu_registers[1] = Core::g_app_core->GetReg(1); + ctx.cpu_registers[2] = Core::g_app_core->GetReg(2); + ctx.cpu_registers[3] = Core::g_app_core->GetReg(3); + ctx.cpu_registers[4] = Core::g_app_core->GetReg(4); + ctx.cpu_registers[5] = Core::g_app_core->GetReg(5); + ctx.cpu_registers[6] = Core::g_app_core->GetReg(6); + ctx.cpu_registers[7] = Core::g_app_core->GetReg(7); + ctx.cpu_registers[8] = Core::g_app_core->GetReg(8); + ctx.cpu_registers[9] = Core::g_app_core->GetReg(9); + ctx.cpu_registers[10] = Core::g_app_core->GetReg(10); + ctx.cpu_registers[11] = Core::g_app_core->GetReg(11); + ctx.cpu_registers[12] = Core::g_app_core->GetReg(12); + ctx.sp = Core::g_app_core->GetReg(13); + ctx.lr = Core::g_app_core->GetReg(14); + ctx.pc = Core::g_app_core->GetPC(); + ctx.cpsr = Core::g_app_core->GetCPSR(); } /// Loads a CPU context -void __KernelLoadContext(ThreadContext *ctx) { - Core::g_app_core->SetReg(0, ctx->reg[0]); - Core::g_app_core->SetReg(1, ctx->reg[1]); - Core::g_app_core->SetReg(2, ctx->reg[2]); - Core::g_app_core->SetReg(3, ctx->reg[3]); - Core::g_app_core->SetReg(4, ctx->reg[4]); - Core::g_app_core->SetReg(5, ctx->reg[5]); - Core::g_app_core->SetReg(6, ctx->reg[6]); - Core::g_app_core->SetReg(7, ctx->reg[7]); - Core::g_app_core->SetReg(8, ctx->reg[8]); - Core::g_app_core->SetReg(9, ctx->reg[9]); - Core::g_app_core->SetReg(10, ctx->reg[10]); - Core::g_app_core->SetReg(11, ctx->reg[11]); - Core::g_app_core->SetReg(12, ctx->reg[12]); - Core::g_app_core->SetReg(13, ctx->reg[13]); - Core::g_app_core->SetReg(14, ctx->reg[14]); - Core::g_app_core->SetReg(15, ctx->reg[15]); - Core::g_app_core->SetPC(ctx->pc); - Core::g_app_core->SetCPSR(ctx->cpsr); +void __KernelLoadContext(const ThreadContext &ctx) { + Core::g_app_core->SetReg(0, ctx.cpu_registers[0]); + Core::g_app_core->SetReg(1, ctx.cpu_registers[1]); + Core::g_app_core->SetReg(2, ctx.cpu_registers[2]); + Core::g_app_core->SetReg(3, ctx.cpu_registers[3]); + Core::g_app_core->SetReg(4, ctx.cpu_registers[4]); + Core::g_app_core->SetReg(5, ctx.cpu_registers[5]); + Core::g_app_core->SetReg(6, ctx.cpu_registers[6]); + Core::g_app_core->SetReg(7, ctx.cpu_registers[7]); + Core::g_app_core->SetReg(8, ctx.cpu_registers[8]); + Core::g_app_core->SetReg(9, ctx.cpu_registers[9]); + Core::g_app_core->SetReg(10, ctx.cpu_registers[10]); + Core::g_app_core->SetReg(11, ctx.cpu_registers[11]); + Core::g_app_core->SetReg(12, ctx.cpu_registers[12]); + Core::g_app_core->SetReg(13, ctx.sp); + Core::g_app_core->SetReg(14, ctx.lr); + //Core::g_app_core->SetReg(15, ctx.pc); + + Core::g_app_core->SetPC(ctx.pc); + Core::g_app_core->SetCPSR(ctx.cpsr); } -/// Switches thread context -void __KernelSwitchContext(Thread *target, const char *reason) { - u32 old_pc = 0; - UID old_uid = 0; - const char *old_name = g_hle_current_thread_name != NULL ? g_hle_current_thread_name : "(none)"; - Thread *cur = __GetCurrentThread(); +/// Resets a thread +void __KernelResetThread(Thread *t, s32 lowest_priority) { + memset(&t->context, 0, sizeof(ThreadContext)); - if (cur) { // It might just have been deleted. - __KernelSaveContext(&cur->context); - old_pc = Core::g_app_core->GetPC(); - old_uid = cur->GetUID(); - - // Normally this is taken care of in __KernelNextThread(). - if (cur->IsRunning()) - __KernelChangeReadyState(cur, old_uid, true); - } - if (target) { - __SetCurrentThread(target, target->GetUID(), target->nt.name); - __KernelChangeReadyState(target, g_current_thread, false); - - target->nt.status = (target->nt.status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - - __KernelLoadContext(&target->context); - } else { - __SetCurrentThread(NULL, 0, NULL); + t->context.pc = t->entry_point; + t->context.sp = t->stack_top; + + if (t->current_priority < lowest_priority) { + t->current_priority = t->initial_priority; } + + t->wait_type = WAITTYPE_NONE; } -bool __KernelSwitchToThread(UID thread_id, const char *reason) { - if (!reason) { - reason = "switch to thread"; - } - if (g_current_thread == thread_id) { - return false; - } - u32 error; - Thread *t = g_kernel_objects.Get(thread_id, error); - if (!t) { - ERROR_LOG(KERNEL, "__KernelSwitchToThread: %x doesn't exist", thread_id); - HLE::ReSchedule("switch to deleted thread"); - } else if (t->IsReady() || t->IsRunning()) { - Thread *current = __GetCurrentThread(); - if (current && current->IsRunning()) { - __KernelChangeReadyState(current, g_current_thread, true); +/// Creates a new thread +Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size=0x4000) { + static u32 _handle_count = 1; + + Thread *t = new Thread; + + handle = (_handle_count++); + + g_thread_queue.push_back(handle); + g_thread_ready_queue.prepare(priority); + + t->status = THREADSTATUS_DORMANT; + t->entry_point = entry_point; + t->stack_top = stack_top; + t->stack_size = stack_size; + t->initial_priority = t->current_priority = priority; + t->processor_id = processor_id; + t->wait_type = WAITTYPE_NONE; + + strncpy(t->name, name, KERNELOBJECT_MAX_NAME_LENGTH); + t->name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + + return t; +} + +/// Change a thread to "ready" state +void __KernelChangeReadyState(Thread *t, bool ready) { + Handle handle = t->GetHandle(); + if (t->IsReady()) { + if (!ready) { + g_thread_ready_queue.remove(t->current_priority, handle); } - __KernelSwitchContext(t, reason); - return true; - } else { - HLE::ReSchedule("switch to waiting thread"); - } - return false; -} - -/// Sets up the root (primary) thread of execution -UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size) { - UID id; - - Thread *thread = __KernelCreateThread(id, module_id, "root", prio, Core::g_app_core->GetPC(), - arg, Memory::SCRATCHPAD_VADDR_END, 0xFFFFFFFE, stack_size=stack_size); - - if (thread->current_stack.start == 0) { - ERROR_LOG(KERNEL, "Unable to allocate stack for root thread."); - } - __KernelResetThread(thread, 0); - - Thread *prev_thread = __GetCurrentThread(); - if (prev_thread && prev_thread->IsRunning()) - __KernelChangeReadyState(g_current_thread, true); - __SetCurrentThread(thread, id, "root"); - thread->nt.status = THREADSTATUS_RUNNING; // do not schedule - - strcpy(thread->nt.name, "root"); - - __KernelLoadContext(&thread->context); - - // NOTE(bunnei): Not sure this is really correct, ignore args for now... - //Core::g_app_core->SetReg(0, args); - //Core::g_app_core->SetReg(13, (args + 0xf) & ~0xf); // Setup SP - probably not correct - //u32 location = Core::g_app_core->GetReg(13); // SP - //Core::g_app_core->SetReg(1, location); - - //if (argp) - // Memory::Memcpy(location, argp, args); - //// Let's assume same as starting a new thread, 64 bytes for safety/kernel. - //Core::g_app_core->SetReg(13, Core::g_app_core->GetReg(13) - 64); - - return id; -} - -int __KernelRotateThreadReadyQueue(int priority) { - Thread *cur = __GetCurrentThread(); - - // 0 is special, it means "my current priority." - if (priority == 0) { - priority = cur->nt.current_priority; - } - //if (priority <= 0x07 || priority > 0x77) - // return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY; - - if (!g_thread_ready_queue.empty(priority)) { - // In other words, yield to everyone else. - if (cur->nt.current_priority == priority) { - g_thread_ready_queue.push_back(priority, g_current_thread); - cur->nt.status = (cur->nt.status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; - - // Yield the next thread of this priority to all other threads of same priority. + } else if (ready) { + if (t->IsRunning()) { + g_thread_ready_queue.push_front(t->current_priority, handle); } else { - g_thread_ready_queue.rotate(priority); + g_thread_ready_queue.push_back(t->current_priority, handle); + } + t->status = THREADSTATUS_READY; + } +} + +/// Changes a threads state +void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) { + if (!t || t->status == new_status) { + return; + } + __KernelChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); + t->status = new_status; + + if (new_status == THREADSTATUS_WAIT) { + if (t->wait_type == WAITTYPE_NONE) { + printf("ERROR: Waittype none not allowed here\n"); } } - HLE::EatCycles(250); - HLE::ReSchedule("rotatethreadreadyqueue"); - - return 0; } +/// Switches CPU context to that of the specified thread +void __KernelSwitchContext(Thread* t, const char *reason) { + Thread *cur = __GetCurrentThread(); + + // Save context for current thread + if (cur) { + __KernelSaveContext(cur->context); + + if (cur->IsRunning()) { + __KernelChangeReadyState(cur, true); + } + } + // Load context of new thread + if (t) { + __SetCurrentThread(t); + __KernelChangeReadyState(t, false); + t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; + t->wait_type = WAITTYPE_NONE; + __KernelLoadContext(t->context); + } else { + __SetCurrentThread(NULL); + } +} + +/// Gets the next thread that is ready to be run by priority +Thread *__KernelNextThread() { + Handle next; + Thread *cur = __GetCurrentThread(); + + if (cur && cur->IsRunning()) { + next = g_thread_ready_queue.pop_first_better(cur->current_priority); + } else { + next = g_thread_ready_queue.pop_first(); + } + if (next < 0) { + return NULL; + } + return g_kernel_objects.GetFast(next); +} + +/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) +void __KernelCallThread(Thread *t) { + // Stop waiting + if (t->wait_type != WAITTYPE_NONE) { + t->wait_type = WAITTYPE_NONE; + } + __KernelChangeThreadState(t, THREADSTATUS_READY); +} + +/// Sets up the primary application thread +Handle __KernelSetupMainThread(s32 priority, int stack_size) { + Handle handle; + + // Initialize new "main" thread + Thread *t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, + 0xFFFFFFFE, Memory::SCRATCHPAD_VADDR_END, stack_size); + + __KernelResetThread(t, 0); + + // If running another thread already, set it to "ready" state + Thread *cur = __GetCurrentThread(); + if (cur && cur->IsRunning()) { + __KernelChangeReadyState(cur, true); + } + + // Run new "main" thread + __SetCurrentThread(t); + t->status = THREADSTATUS_RUNNING; + __KernelLoadContext(t->context); + + return handle; +} + +/// Resumes a thread from waiting by marking it as "ready" +void __KernelResumeThreadFromWait(Handle handle) { + u32 error; + Thread *t = g_kernel_objects.Get(handle, error); + if (t) { + t->status &= ~THREADSTATUS_WAIT; + if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { + __KernelChangeReadyState(t, true); + } + } +} + +/// Puts a thread in the wait state for the given type/reason +void __KernelWaitCurThread(WaitType wait_type, const char *reason) { + Thread *t = __GetCurrentThread(); + t->wait_type = wait_type; + __KernelChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); +} + +/// Reschedules to the next available thread (call after current thread is suspended) +void __KernelReschedule(const char *reason) { + Thread *next = __KernelNextThread(); + if (next > 0) { + __KernelSwitchContext(next, reason); + } +} + + void __KernelThreadingInit() { } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 05468fb2e0..cca4e85fd5 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -7,50 +7,10 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" -enum ThreadStatus { - THREADSTATUS_RUNNING = 1, - THREADSTATUS_READY = 2, - THREADSTATUS_WAIT = 4, - THREADSTATUS_SUSPEND = 8, - THREADSTATUS_DORMANT = 16, - THREADSTATUS_DEAD = 32, - - THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND -}; - -struct ThreadContext { - void reset(); - - u32 reg[16]; - u32 cpsr; - u32 pc; -}; - class Thread; -Thread* __KernelCreateThread(UID& id, UID module_id, const char* name, u32 priority, u32 entrypoint, - u32 arg, u32 stack_top, u32 processor_id, int stack_size=0x4000); - -UID __KernelCreateThread(UID module_id, const char* name, u32 priority, u32 entry_point, u32 arg, - u32 stack_top, u32 processor_id, int stack_size=0x4000); - -void __KernelResetThread(Thread *t, int lowest_priority); -void __KernelChangeReadyState(Thread *thread, UID thread_id, bool ready); -void __KernelChangeReadyState(UID thread_id, bool ready); -Thread* __KernelNextThread(); -void __KernelSaveContext(ThreadContext *ctx); -void __KernelLoadContext(ThreadContext *ctx); -void __KernelSwitchContext(Thread *target, const char *reason); -bool __KernelSwitchToThread(UID thread_id, const char *reason); -UID __KernelSetupRootThread(UID module_id, int arg, int prio, int stack_size=0x4000); -int __KernelRotateThreadReadyQueue(int priority=0); +/// Sets up the primary application thread +Handle __KernelSetupMainThread(s32 priority, int stack_size=0x4000); void __KernelThreadingInit(); void __KernelThreadingShutdown(); - -//const char *__KernelGetThreadName(SceUID threadID); -// -//void __KernelSaveContext(ThreadContext *ctx); -//void __KernelLoadContext(ThreadContext *ctx); - -//void __KernelSwitchContext(Thread *target, const char *reason); \ No newline at end of file From 3c240abf10c7f4f47cc6a8ad2ba6ee060abaa96b Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 May 2014 18:54:57 -0400 Subject: [PATCH 20/72] added ability to load AXF files (same as ELF) --- src/citra_qt/main.cpp | 2 +- src/core/loader.cpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 76e0c68c36..9be9829093 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -142,7 +142,7 @@ void GMainWindow::BootGame(const char* filename) void GMainWindow::OnMenuLoadFile() { - QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.dat *.bin)")); + QString filename = QFileDialog::getOpenFileName(this, tr("Load file"), QString(), tr("3DS homebrew (*.elf *.axf *.dat *.bin)")); if (filename.size()) BootGame(filename.toLatin1().data()); } diff --git a/src/core/loader.cpp b/src/core/loader.cpp index a1f5dcdb59..444b75feba 100644 --- a/src/core/loader.cpp +++ b/src/core/loader.cpp @@ -187,6 +187,9 @@ FileType IdentifyFile(std::string &filename) { else if (!strcasecmp(extension.c_str(), ".elf")) { return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p } + else if (!strcasecmp(extension.c_str(), ".axf")) { + return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p + } else if (!strcasecmp(extension.c_str(), ".bin")) { return FILETYPE_CTR_BIN; } From 4910b6f3369f33a2dc6728e4156a62368c2c9cb1 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 May 2014 18:56:28 -0400 Subject: [PATCH 21/72] added memory mapped region for system mem - sdk demos load a segment here on ELF load --- src/core/mem_map.cpp | 3 +++ src/core/mem_map.h | 7 +++++++ src/core/mem_map_funcs.cpp | 14 +++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp index 59560b87d4..c45746be90 100644 --- a/src/core/mem_map.cpp +++ b/src/core/mem_map.cpp @@ -17,6 +17,7 @@ u8* g_base = NULL; ///< The base pointer to the aut MemArena g_arena; ///< The MemArena class u8* g_exefs_code = NULL; ///< ExeFS:/.code is loaded here +u8* g_system_mem = NULL; ///< System memory u8* g_heap = NULL; ///< Application heap (main memory) u8* g_heap_gsp = NULL; ///< GSP heap (main memory) u8* g_vram = NULL; ///< Video memory (VRAM) pointer @@ -27,6 +28,7 @@ u8* g_physical_bootrom = NULL; ///< Bootrom physical memory u8* g_uncached_bootrom = NULL; u8* g_physical_exefs_code = NULL; ///< Phsical ExeFS:/.code is loaded here +u8* g_physical_system_mem = NULL; ///< System physical memory u8* g_physical_fcram = NULL; ///< Main physical memory (FCRAM) u8* g_physical_heap_gsp = NULL; ///< GSP heap physical memory u8* g_physical_vram = NULL; ///< Video physical memory (VRAM) @@ -39,6 +41,7 @@ static MemoryView g_views[] = { {&g_vram, &g_physical_vram, VRAM_VADDR, VRAM_SIZE, 0}, {&g_heap, &g_physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM}, {&g_shared_mem, &g_physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0}, + {&g_system_mem, &g_physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0}, {&g_kernel_mem, &g_physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0}, {&g_heap_gsp, &g_physical_heap_gsp, HEAP_GSP_VADDR, HEAP_GSP_SIZE, 0}, }; diff --git a/src/core/mem_map.h b/src/core/mem_map.h index af2212a5f1..12d497ef3f 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h @@ -47,6 +47,12 @@ enum { EXEFS_CODE_VADDR_END = (EXEFS_CODE_VADDR + EXEFS_CODE_SIZE), EXEFS_CODE_MASK = 0x03FFFFFF, + // Region of FCRAM used by system + SYSTEM_MEMORY_SIZE = 0x02C00000, ///< 44MB + SYSTEM_MEMORY_VADDR = 0x04000000, + SYSTEM_MEMORY_VADDR_END = (SYSTEM_MEMORY_VADDR + SYSTEM_MEMORY_SIZE), + SYSTEM_MEMORY_MASK = 0x03FFFFFF, + HEAP_SIZE = FCRAM_SIZE, ///< Application heap size //HEAP_PADDR = HEAP_GSP_SIZE, //HEAP_PADDR_END = (HEAP_PADDR + HEAP_SIZE), @@ -116,6 +122,7 @@ extern u8* g_heap; ///< Application heap (main memory) extern u8* g_vram; ///< Video memory (VRAM) extern u8* g_shared_mem; ///< Shared memory extern u8* g_kernel_mem; ///< Kernel memory +extern u8* g_system_mem; ///< System memory extern u8* g_exefs_code; ///< ExeFS:/.code is loaded here void Init(); diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp index 8ab647714f..86e9eaa203 100644 --- a/src/core/mem_map_funcs.cpp +++ b/src/core/mem_map_funcs.cpp @@ -73,6 +73,10 @@ inline void _Read(T &var, const u32 addr) { } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { var = *((const T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK]); + // System memory + } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { + var = *((const T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK]); + // Config memory } else if ((vaddr >= CONFIG_MEMORY_VADDR) && (vaddr < CONFIG_MEMORY_VADDR_END)) { ConfigMem::Read(var, vaddr); @@ -115,6 +119,10 @@ inline void _Write(u32 addr, const T data) { } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { *(T*)&g_shared_mem[vaddr & SHARED_MEMORY_MASK] = data; + // System memory + } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { + *(T*)&g_system_mem[vaddr & SYSTEM_MEMORY_MASK] = data; + // VRAM } else if ((vaddr >= VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { *(T*)&g_vram[vaddr & VRAM_MASK] = data; @@ -153,9 +161,13 @@ u8 *GetPointer(const u32 addr) { return g_heap + (vaddr & HEAP_MASK); // Shared memory - } else if ((vaddr > SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { + } else if ((vaddr >= SHARED_MEMORY_VADDR) && (vaddr < SHARED_MEMORY_VADDR_END)) { return g_shared_mem + (vaddr & SHARED_MEMORY_MASK); + // System memory + } else if ((vaddr >= SYSTEM_MEMORY_VADDR) && (vaddr < SYSTEM_MEMORY_VADDR_END)) { + return g_system_mem + (vaddr & SYSTEM_MEMORY_MASK); + // VRAM } else if ((vaddr > VRAM_VADDR) && (vaddr < VRAM_VADDR_END)) { return g_vram + (vaddr & VRAM_MASK); From 4fba4f36bf20c2721e2602c450eafcc1117ac643 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 15 May 2014 20:17:30 -0400 Subject: [PATCH 22/72] - added SVC stubs for QueryMemory and GetThreadId - added SVC structs MemoryInfo and PageInfo --- src/core/hle/function_wrappers.h | 5 +++++ src/core/hle/syscall.cpp | 16 ++++++++++++++-- src/core/hle/syscall.h | 11 +++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 83be7648b2..61790e5a3f 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -734,6 +734,11 @@ template void WrapI_VU(){ RETURN(retval); } +template void WrapI_VVU(){ + u32 retval = func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)); + RETURN(retval); +} + template void WrapI_VUVI(){ u32 retval = func(Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), PARAM(3)); RETURN(retval); diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index 0700d9e827..0765bce7a9 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -169,10 +169,22 @@ Result ReleaseMutex(Handle handle) { return 0; } +Result GetThreadId(void* thread_id, u32 thread) { + DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetThreadId called thread=0x%08X", thread); + return 0; +} + +Result QueryMemory(void *_info, void *_out, u32 addr) { + MemoryInfo* info = (MemoryInfo*) _info; + PageInfo* out = (PageInfo*) _out; + DEBUG_LOG(SVC, "(UNIMPLEMENTED) QueryMemory called addr=0x%08X", addr); + return 0; +} + const HLE::FunctionDef Syscall_Table[] = { {0x00, NULL, "Unknown"}, {0x01, WrapI_VUUUUU, "ControlMemory"}, - {0x02, NULL, "QueryMemory"}, + {0x02, WrapI_VVU, "QueryMemory"}, {0x03, NULL, "ExitProcess"}, {0x04, NULL, "GetProcessAffinityMask"}, {0x05, NULL, "SetProcessAffinityMask"}, @@ -225,7 +237,7 @@ const HLE::FunctionDef Syscall_Table[] = { {0x34, NULL, "OpenThread"}, {0x35, NULL, "GetProcessId"}, {0x36, NULL, "GetProcessIdOfThread"}, - {0x37, NULL, "GetThreadId"}, + {0x37, WrapI_VU, "GetThreadId"}, {0x38, WrapI_VU, "GetResourceLimit"}, {0x39, NULL, "GetResourceLimitLimitValues"}, {0x3A, WrapI_VUVI, "GetResourceLimitCurrentValues"}, diff --git a/src/core/hle/syscall.h b/src/core/hle/syscall.h index 15af5e1384..17f1902661 100644 --- a/src/core/hle/syscall.h +++ b/src/core/hle/syscall.h @@ -9,6 +9,17 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// // SVC structures +struct MemoryInfo { + u32 base_address; + u32 size; + u32 permission; + u32 state; +}; + +struct PageInfo { + u32 flags; +}; + struct ThreadContext { u32 cpu_registers[13]; u32 sp; From 540a693eae210d090b87426ead8cfac5893a9ef8 Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 16 May 2014 23:23:56 -0400 Subject: [PATCH 23/72] updated APT_U::GetLockHandle to return a valid handle --- src/core/hle/service/apt.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp index 709ac54935..ddb975607c 100644 --- a/src/core/hle/service/apt.cpp +++ b/src/core/hle/service/apt.cpp @@ -19,7 +19,11 @@ void Initialize(Service::Interface* self) { void GetLockHandle(Service::Interface* self) { u32* cmd_buff = Service::GetCommandBuffer(); - cmd_buff[5] = 0x00000000; // TODO: This should be an actual mutex handle + u32 flags = cmd_buff[1]; + + // TODO: This should be an actual mutex handle. Games will check that this is not non-zero + // (NULL), and fail if such. A faked non-zero value will at least enable further booting. + cmd_buff[5] = 0xDEADBEEF; } const Interface::FunctionInfo FunctionTable[] = { From 4faed70b40ae44a0a5c63366ad4e6dfa6340e73f Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 16 May 2014 23:24:39 -0400 Subject: [PATCH 24/72] add kernel.* and thread.* to VS filters --- src/core/core.vcxproj.filters | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index edf34ce2fc..6aedeb54b0 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -31,6 +31,9 @@ {13ef9860-2ba0-47e9-a93d-b4052adab269} + + {8089d94b-5faa-43dc-854b-ffd2fa2e7fe3} + @@ -153,6 +156,12 @@ arm\interpreter\mmu + + hle\kernel + + + hle\kernel + @@ -274,6 +283,12 @@ arm\interpreter\mmu + + hle\kernel + + + hle\kernel + From 39ee75fc8d2a3291195448696f6a9bda8b1d58ad Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 16 May 2014 23:25:16 -0400 Subject: [PATCH 25/72] added stubbed GetProcSemaphore - does nothing but avoids an exception --- src/core/hle/service/srv.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 0717414444..303a943d4f 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -16,6 +16,12 @@ void Initialize(Service::Interface* self) { NOTICE_LOG(OSHLE, "SRV::Sync - Initialize"); } +void GetProcSemaphore(Service::Interface* self) { + // Get process semaphore? + u32* cmd_buff = Service::GetCommandBuffer(); + cmd_buff[3] = 0xDEADBEEF; // Return something... 0 == NULL, raises an exception +} + void GetServiceHandle(Service::Interface* self) { Syscall::Result res = 0; u32* cmd_buff = Service::GetCommandBuffer(); @@ -39,7 +45,7 @@ void GetServiceHandle(Service::Interface* self) { const Interface::FunctionInfo FunctionTable[] = { {0x00010002, Initialize, "Initialize"}, - {0x00020000, NULL, "GetProcSemaphore"}, + {0x00020000, GetProcSemaphore, "GetProcSemaphore"}, {0x00030100, NULL, "RegisterService"}, {0x000400C0, NULL, "UnregisterService"}, {0x00050100, GetServiceHandle, "GetServiceHandle"}, From 7cdb70505944b2ed456d7f5376594e05f3b3357f Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 16 May 2014 23:48:15 -0400 Subject: [PATCH 26/72] - replaced KERNELOBJECT_MAX_NAME_LENGTH with KERNEL_MAX_NAME_LENGTH - added KERNEL_DEFAULT_STACK_SIZE definition (0x4000) --- src/core/hle/kernel/kernel.h | 5 ++--- src/core/hle/kernel/thread.cpp | 9 +++++---- src/core/hle/kernel/thread.h | 6 +++++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 24d422682e..2608eecc91 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -16,11 +16,10 @@ enum KernelIDType { }; enum { - KERNELOBJECT_MAX_NAME_LENGTH = 255, + KERNEL_MAX_NAME_LENGTH = 0x100, + KERNEL_DEFAULT_STACK_SIZE = 0x4000, }; -#define KERNELOBJECT_MAX_NAME_LENGTH 31 - class KernelObjectPool; class KernelObject { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 833a1b4bad..76a73747d8 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -81,7 +81,7 @@ public: WaitType wait_type; - char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; + char name[KERNEL_MAX_NAME_LENGTH+1]; }; // Lists all thread ids that aren't deleted/etc. @@ -165,7 +165,8 @@ void __KernelResetThread(Thread *t, s32 lowest_priority) { } /// Creates a new thread -Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size=0x4000) { +Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, + s32 processor_id, u32 stack_top, int stack_size) { static u32 _handle_count = 1; Thread *t = new Thread; @@ -183,8 +184,8 @@ Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, t->processor_id = processor_id; t->wait_type = WAITTYPE_NONE; - strncpy(t->name, name, KERNELOBJECT_MAX_NAME_LENGTH); - t->name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH); + t->name[KERNEL_MAX_NAME_LENGTH] = '\0'; return t; } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index cca4e85fd5..add6107d76 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -9,8 +9,12 @@ class Thread; +/// Creates a new thread +Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, + s32 processor_id, u32 stack_top, int stack_size=KERNEL_DEFAULT_STACK_SIZE); + /// Sets up the primary application thread -Handle __KernelSetupMainThread(s32 priority, int stack_size=0x4000); +Handle __KernelSetupMainThread(s32 priority, int stack_size=KERNEL_DEFAULT_STACK_SIZE); void __KernelThreadingInit(); void __KernelThreadingShutdown(); From 43bc2ac724e1e1340827dd777ec4dbb6b94b4603 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 May 2014 00:53:41 -0400 Subject: [PATCH 27/72] added MIN, MAX, and CLAMP macros to common_funcs --- src/common/common_funcs.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index f8d10eb3e5..dca4dc47fb 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -22,6 +22,11 @@ template<> struct CompileTimeAssert {}; #define b32(x) (b16(x) | (b16(x) >>16) ) #define ROUND_UP_POW2(x) (b32(x - 1) + 1) +#define MIN(a, b) ((a)<(b)?(a):(b)) +#define MAX(a, b) ((a)>(b)?(a):(b)) + +#define CLAMP(x, min, max) (((x) > max) ? max : (((x) < min) ? min : (x))) + #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #ifndef _WIN32 From 14ae026386cf3f984d60401e2104165c3ca3266b Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 May 2014 00:56:00 -0400 Subject: [PATCH 28/72] - added enum ThreadProcessorId - reorganized some kernel thread functions - added placeholder __KernelWaitThread_Synchronization function --- src/core/hle/kernel/thread.cpp | 141 +++++++++++++++++++++------------ src/core/hle/kernel/thread.h | 19 ++++- 2 files changed, 107 insertions(+), 53 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 76a73747d8..b967b3c624 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -21,20 +21,14 @@ // Enums -enum ThreadPriority { - THREADPRIO_HIGHEST = 0, - THREADPRIO_DEFAULT = 16, - THREADPRIO_LOWEST = 31, -}; - enum ThreadStatus { - THREADSTATUS_RUNNING = 1, - THREADSTATUS_READY = 2, - THREADSTATUS_WAIT = 4, - THREADSTATUS_SUSPEND = 8, - THREADSTATUS_DORMANT = 16, - THREADSTATUS_DEAD = 32, - THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND + THREADSTATUS_RUNNING = 1, + THREADSTATUS_READY = 2, + THREADSTATUS_WAIT = 4, + THREADSTATUS_SUSPEND = 8, + THREADSTATUS_DORMANT = 16, + THREADSTATUS_DEAD = 32, + THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND }; enum WaitType { @@ -46,8 +40,6 @@ enum WaitType { WAITTYPE_VBLANK, WAITTYPE_MUTEX, WAITTYPE_SYNCH, - - NUM_WAITTYPES }; typedef s32 Handle; @@ -164,32 +156,6 @@ void __KernelResetThread(Thread *t, s32 lowest_priority) { t->wait_type = WAITTYPE_NONE; } -/// Creates a new thread -Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, - s32 processor_id, u32 stack_top, int stack_size) { - static u32 _handle_count = 1; - - Thread *t = new Thread; - - handle = (_handle_count++); - - g_thread_queue.push_back(handle); - g_thread_ready_queue.prepare(priority); - - t->status = THREADSTATUS_DORMANT; - t->entry_point = entry_point; - t->stack_top = stack_top; - t->stack_size = stack_size; - t->initial_priority = t->current_priority = priority; - t->processor_id = processor_id; - t->wait_type = WAITTYPE_NONE; - - strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH); - t->name[KERNEL_MAX_NAME_LENGTH] = '\0'; - - return t; -} - /// Change a thread to "ready" state void __KernelChangeReadyState(Thread *t, bool ready) { Handle handle = t->GetHandle(); @@ -222,6 +188,79 @@ void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) { } } +/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) +void __KernelCallThread(Thread *t) { + // Stop waiting + if (t->wait_type != WAITTYPE_NONE) { + t->wait_type = WAITTYPE_NONE; + } + __KernelChangeThreadState(t, THREADSTATUS_READY); +} + +/// Creates a new thread +Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, + s32 processor_id, u32 stack_top, int stack_size) { + + Thread *t = new Thread; + + handle = g_kernel_objects.Create(t); + + g_thread_queue.push_back(handle); + g_thread_ready_queue.prepare(priority); + + t->status = THREADSTATUS_DORMANT; + t->entry_point = entry_point; + t->stack_top = stack_top; + t->stack_size = stack_size; + t->initial_priority = t->current_priority = priority; + t->processor_id = processor_id; + t->wait_type = WAITTYPE_NONE; + + strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH); + t->name[KERNEL_MAX_NAME_LENGTH] = '\0'; + + return t; +} + +/// Creates a new thread - wrapper for external user +Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 processor_id, + u32 stack_top, int stack_size) { + if (name == NULL) { + ERROR_LOG(KERNEL, "__KernelCreateThread(): NULL name"); + return -1; + } + if ((u32)stack_size < 0x200) { + ERROR_LOG(KERNEL, "__KernelCreateThread(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); + WARN_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", + 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)) { + ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid entry %08x", name, entry_point); + return -1; + } + Handle handle; + Thread *t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top, + stack_size); + + HLE::EatCycles(32000); + + // This won't schedule to the new thread, but it may to one woken from eating cycles. + // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. + HLE::ReSchedule("thread created"); + + __KernelCallThread(t); + + return handle; +} + /// Switches CPU context to that of the specified thread void __KernelSwitchContext(Thread* t, const char *reason) { Thread *cur = __GetCurrentThread(); @@ -262,22 +301,13 @@ Thread *__KernelNextThread() { return g_kernel_objects.GetFast(next); } -/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void __KernelCallThread(Thread *t) { - // Stop waiting - if (t->wait_type != WAITTYPE_NONE) { - t->wait_type = WAITTYPE_NONE; - } - __KernelChangeThreadState(t, THREADSTATUS_READY); -} - /// Sets up the primary application thread Handle __KernelSetupMainThread(s32 priority, int stack_size) { Handle handle; // Initialize new "main" thread Thread *t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, - 0xFFFFFFFE, Memory::SCRATCHPAD_VADDR_END, stack_size); + THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); __KernelResetThread(t, 0); @@ -322,6 +352,15 @@ void __KernelReschedule(const char *reason) { } } +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Wait thread - on WaitSynchronization +void __KernelWaitThread_Synchronization() { + // TODO(bunnei): Just a placeholder function for now... FixMe + __KernelWaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// void __KernelThreadingInit() { } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index add6107d76..8138be26f7 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -9,8 +9,20 @@ class Thread; -/// Creates a new thread -Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, +enum ThreadPriority { + THREADPRIO_HIGHEST = 0, + THREADPRIO_DEFAULT = 16, + THREADPRIO_LOWEST = 31, +}; + +enum ThreadProcessorId { + THREADPROCESSORID_0 = 0xFFFFFFFE, + THREADPROCESSORID_1 = 0xFFFFFFFD, + THREADPROCESSORID_ALL = 0xFFFFFFFC, +}; + +/// Creates a new thread - wrapper for external user +Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size=KERNEL_DEFAULT_STACK_SIZE); /// Sets up the primary application thread @@ -18,3 +30,6 @@ Handle __KernelSetupMainThread(s32 priority, int stack_size=KERNEL_DEFAULT_STACK void __KernelThreadingInit(); void __KernelThreadingShutdown(); + +/// Wait thread - on WaitSynchronization +void __KernelWaitThread_Synchronization(); From cfea5fdd5878968e8a1de99b86966c57d0fc5697 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 May 2014 13:47:44 -0400 Subject: [PATCH 29/72] cleanups to SVC CreateThread --- src/core/hle/syscall.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index 0765bce7a9..0c78b19fb0 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -6,6 +6,9 @@ #include "core/mem_map.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/thread.h" + #include "core/hle/function_wrappers.h" #include "core/hle/syscall.h" #include "core/hle/service/service.h" @@ -140,19 +143,23 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* return 0; } -Result CreateThread(void* thread, u32 thread_priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { - std::string thread_name; +Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 stack_top, + u32 processor_id) { + std::string name; if (Symbols::HasSymbol(entry_point)) { TSymbol symbol = Symbols::GetSymbol(entry_point); - thread_name = symbol.name; + name = symbol.name; } else { char buff[100]; - sprintf(buff, "%s", "unk-%08X", entry_point); - thread_name = buff; + sprintf(buff, "%s", "unknown-%08X", entry_point); + name = buff; } - DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, " - "stacktop=0x%08X, threadpriority=0x%08X, processorid=0x%08X", entry_point, - thread_name.c_str(), arg, stack_top, thread_priority, processor_id); + DEBUG_LOG(SVC, "CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " + "threadpriority=0x%08X, processorid=0x%08X", entry_point, name.c_str(), arg, stack_top, + priority, processor_id); + + Handle handle = __KernelCreateThread(name.c_str(), entry_point, priority, processor_id, + stack_top); return 0; } From 09b8e8fb6afbbcc3dd6127ee02f7ac1611eb85aa Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 May 2014 13:47:55 -0400 Subject: [PATCH 30/72] changed a comment --- src/core/hle/kernel/thread.cpp | 2 +- src/core/hle/kernel/thread.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b967b3c624..d0bc9c8d84 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -354,7 +354,7 @@ void __KernelReschedule(const char *reason) { //////////////////////////////////////////////////////////////////////////////////////////////////// -/// Wait thread - on WaitSynchronization +/// Put current thread in a wait state - on WaitSynchronization void __KernelWaitThread_Synchronization() { // TODO(bunnei): Just a placeholder function for now... FixMe __KernelWaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 8138be26f7..0d0f465000 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -31,5 +31,5 @@ Handle __KernelSetupMainThread(s32 priority, int stack_size=KERNEL_DEFAULT_STACK void __KernelThreadingInit(); void __KernelThreadingShutdown(); -/// Wait thread - on WaitSynchronization +/// Put current thread in a wait state - on WaitSynchronization void __KernelWaitThread_Synchronization(); From fbc04ad0c7c2bafde1b7b50957799c7bbab8f77b Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 May 2014 13:48:27 -0400 Subject: [PATCH 31/72] ignore thumbemu 0xDEADCODE debugging catch on MCR --- src/core/arm/interpreter/armemu.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index 32e315f4b8..aa1ff17bb2 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp @@ -4478,8 +4478,7 @@ ARMul_Emulate26 (ARMul_State * state) isize) & R15PCBITS)); #endif - } - else + } else if (instr != 0xDEADC0DE) // thumbemu uses 0xDEADCODE for debugging to catch non updates ARMul_MCR (state, instr, DEST); } From 6a6c7eeccbf2e9a766ad6b942f25b3ef6e008944 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 17 May 2014 23:37:25 -0400 Subject: [PATCH 32/72] added stubbed function for WaitSynchronizationN --- src/core/hle/function_wrappers.h | 7 ++++++- src/core/hle/syscall.cpp | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 61790e5a3f..24cc74fc60 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -750,6 +750,11 @@ template void WrapI_VUUUUU(){ } template void WrapI_US64() { - int retval = func(PARAM(0), PARAM64(2)); + int retval = func(PARAM(0), PARAM64(1)); + RETURN(retval); +} + +template void WrapI_VVUUS64() { + int retval = func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2), PARAM(3), PARAM(4)); RETURN(retval); } diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index 0c78b19fb0..728679378e 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -104,10 +104,20 @@ Result CloseHandle(Handle handle) { } /// Wait for a handle to synchronize, timeout after the specified nanoseconds -Result WaitSynchronization1(Handle handle, s64 nanoseconds) { +Result WaitSynchronization1(Handle handle, s64 nano_seconds) { // ImplementMe DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d", - handle, nanoseconds); + handle, nano_seconds); + return 0; +} + +/// Wait for the given handles to synchronize, timeout after the specified nanoseconds +Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wait_all, s64 nano_seconds) { + s32* out = (s32*)_out; + Handle* handles = (Handle*)_handles; + // ImplementMe + DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronizationN called handle_count=%d, wait_all=%d, nanoseconds=%d", + handle_count, wait_all, nano_seconds); return 0; } @@ -226,7 +236,7 @@ const HLE::FunctionDef Syscall_Table[] = { {0x22, NULL, "ArbitrateAddress"}, {0x23, WrapI_U, "CloseHandle"}, {0x24, WrapI_US64, "WaitSynchronization1"}, - {0x25, NULL, "WaitSynchronizationN"}, + {0x25, WrapI_VVUUS64, "WaitSynchronizationN"}, {0x26, NULL, "SignalAndWait"}, {0x27, NULL, "DuplicateHandle"}, {0x28, NULL, "GetSystemTick"}, From 8fba88d5d59ad4cd89a343e1562a314874bd88a4 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 May 2014 17:52:02 -0400 Subject: [PATCH 33/72] - added stub for CreateEvent - changed some stubbed SVCs to return unique handle names for debugging purposes --- src/core/hle/syscall.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index 728679378e..047d8c40fc 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -3,6 +3,9 @@ // Refer to the license.txt file included. #include +#include + +#include "common/symbols.h" #include "core/mem_map.h" @@ -14,8 +17,6 @@ #include "core/hle/service/service.h" #include "core/hle/kernel/thread.h" -#include "common/symbols.h" - //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Syscall @@ -81,7 +82,6 @@ Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherper /// Connect to an OS service given the port name, returns the handle to the port to out Result ConnectToPort(void* out, const char* port_name) { - Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); Core::g_app_core->SetReg(1, service->GetUID()); DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name); @@ -116,8 +116,13 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa s32* out = (s32*)_out; Handle* handles = (Handle*)_handles; // ImplementMe - DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronizationN called handle_count=%d, wait_all=%d, nanoseconds=%d", - handle_count, wait_all, nano_seconds); + + DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronizationN called handle_count=%d, wait_all=%s, nanoseconds=%d %s", + handle_count, (wait_all ? "true" : "false"), nano_seconds); + + for (int i = 0; i < handle_count; i++) { + DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]); + } return 0; } @@ -125,7 +130,7 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa Result CreateAddressArbiter(void* arbiter) { // ImplementMe DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateAddressArbiter called"); - Core::g_app_core->SetReg(1, 0xDEADBEEF); + Core::g_app_core->SetReg(1, 0xFABBDADD); return 0; } @@ -170,7 +175,8 @@ Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 st Handle handle = __KernelCreateThread(name.c_str(), entry_point, priority, processor_id, stack_top); - + Core::g_app_core->SetReg(1, 0xFEEDDEAF); + return 0; } @@ -178,6 +184,7 @@ Result CreateMutex(void* _mutex, u32 initial_locked) { Handle* mutex = (Handle*)_mutex; DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateMutex called initial_locked=%s", initial_locked ? "true" : "false"); + Core::g_app_core->SetReg(1, 0xF00D0BAD); return 0; } @@ -198,6 +205,13 @@ Result QueryMemory(void *_info, void *_out, u32 addr) { return 0; } +Result CreateEvent(void* _event, u32 reset_type) { + Handle* event = (Handle*)_event; + DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateEvent called reset_type=0x%08X", reset_type); + Core::g_app_core->SetReg(1, 0xBADC0DE0); + return 0; +} + const HLE::FunctionDef Syscall_Table[] = { {0x00, NULL, "Unknown"}, {0x01, WrapI_VUUUUU, "ControlMemory"}, @@ -222,7 +236,7 @@ const HLE::FunctionDef Syscall_Table[] = { {0x14, WrapI_U, "ReleaseMutex"}, {0x15, NULL, "CreateSemaphore"}, {0x16, NULL, "ReleaseSemaphore"}, - {0x17, NULL, "CreateEvent"}, + {0x17, WrapI_VU, "CreateEvent"}, {0x18, NULL, "SignalEvent"}, {0x19, NULL, "ClearEvent"}, {0x1A, NULL, "CreateTimer"}, From 772abad77803809d8ee857efc0d7e29c36c6b2cb Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 May 2014 18:12:29 -0400 Subject: [PATCH 34/72] - moved Handle/Result definitions to kernel.h - added ResetType enum --- src/core/hle/kernel/kernel.h | 3 ++- src/core/hle/kernel/thread.cpp | 2 -- src/core/hle/service/service.h | 12 +++++++----- src/core/hle/service/srv.cpp | 2 +- src/core/hle/service/srv.h | 2 +- src/core/hle/syscall.h | 12 ++++++++---- 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 2608eecc91..d4bb28c728 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -6,7 +6,8 @@ #include "common/common_types.h" -typedef s32 Handle; +typedef u32 Handle; +typedef s32 Result; enum KernelIDType { KERNEL_ID_TYPE_THREAD = 1, diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index d0bc9c8d84..634218e8ba 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -42,8 +42,6 @@ enum WaitType { WAITTYPE_SYNCH, }; -typedef s32 Handle; - class Thread : public KernelObject { public: diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index b260a290a0..026e3d5de1 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -11,6 +11,8 @@ #include "common/common.h" #include "common/common_types.h" #include "core/mem_map.h" + +#include "core/hle/kernel/kernel.h" #include "core/hle/syscall.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -70,14 +72,14 @@ public: } /// Allocates a new handle for the service - Syscall::Handle NewHandle() { - Syscall::Handle handle = (m_handles.size() << 16) | m_uid; + Handle NewHandle() { + Handle handle = (m_handles.size() << 16) | m_uid; m_handles.push_back(handle); return handle; } /// Frees a handle from the service - void DeleteHandle(Syscall::Handle handle) { + void DeleteHandle(Handle handle) { for(auto iter = m_handles.begin(); iter != m_handles.end(); ++iter) { if(*iter == handle) { m_handles.erase(iter); @@ -90,7 +92,7 @@ public: * Called when svcSendSyncRequest is called, loads command buffer and executes comand * @return Return result of svcSendSyncRequest passed back to user app */ - Syscall::Result Sync() { + Result Sync() { u32* cmd_buff = GetCommandBuffer(); auto itr = m_functions.find(cmd_buff[0]); @@ -124,7 +126,7 @@ protected: private: u32 m_uid; - std::vector m_handles; + std::vector m_handles; std::map m_functions; }; diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index 303a943d4f..a3d0411767 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -23,7 +23,7 @@ void GetProcSemaphore(Service::Interface* self) { } void GetServiceHandle(Service::Interface* self) { - Syscall::Result res = 0; + Result res = 0; u32* cmd_buff = Service::GetCommandBuffer(); std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index 760c976b48..f465ebc06f 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/srv.h @@ -30,7 +30,7 @@ public: * Called when svcSendSyncRequest is called, loads command buffer and executes comand * @return Return result of svcSendSyncRequest passed back to user app */ - Syscall::Result Sync(); + Result Sync(); }; diff --git a/src/core/hle/syscall.h b/src/core/hle/syscall.h index 17f1902661..3da349ed5f 100644 --- a/src/core/hle/syscall.h +++ b/src/core/hle/syscall.h @@ -7,7 +7,7 @@ #include "common/common_types.h" //////////////////////////////////////////////////////////////////////////////////////////////////// -// SVC structures +// SVC types struct MemoryInfo { u32 base_address; @@ -31,14 +31,18 @@ struct ThreadContext { u32 fpexc; }; +enum ResetType { + RESETTYPE_ONESHOT, + RESETTYPE_STICKY, + RESETTYPE_PULSE, + RESETTYPE_MAX_BIT = (1u << 31), +}; + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Syscall namespace Syscall { -typedef u32 Handle; -typedef s32 Result; - void Register(); } // namespace From 725d240bf7b9cb48de7a66f8696695ef7aabc889 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 May 2014 18:24:24 -0400 Subject: [PATCH 35/72] renamed "UID" to "Handle" where appropriate --- src/core/hle/service/service.cpp | 16 ++++++++-------- src/core/hle/service/service.h | 22 ++++++++++------------ src/core/hle/service/srv.cpp | 4 ++-- src/core/hle/syscall.cpp | 4 ++-- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index e6605a398a..5601e59a1e 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -32,27 +32,27 @@ Manager::~Manager() { /// Add a service to the manager (does not create it though) void Manager::AddService(Interface* service) { int index = m_services.size(); - u32 new_uid = GetUIDFromIndex(index); + Handle handle = GetHandleFromIndex(index); m_services.push_back(service); - m_port_map[service->GetPortName()] = new_uid; - service->m_uid = new_uid; + m_port_map[service->GetPortName()] = handle; + service->m_handle = handle; } /// Removes a service from the manager, also frees memory void Manager::DeleteService(std::string port_name) { auto service = FetchFromPortName(port_name); - m_services.erase(m_services.begin() + GetIndexFromUID(service->m_uid)); + m_services.erase(m_services.begin() + GetIndexFromHandle(service->m_handle)); m_port_map.erase(port_name); delete service; } -/// Get a Service Interface from its UID -Interface* Manager::FetchFromUID(u32 uid) { - int index = GetIndexFromUID(uid); +/// Get a Service Interface from its Handle +Interface* Manager::FetchFromHandle(Handle handle) { + int index = GetIndexFromHandle(handle); if (index < (int)m_services.size()) { return m_services[index]; } @@ -65,7 +65,7 @@ Interface* Manager::FetchFromPortName(std::string port_name) { if (itr == m_port_map.end()) { return NULL; } - return FetchFromUID(itr->second); + return FetchFromHandle(itr->second); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 026e3d5de1..c3dbd202fc 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -20,8 +20,6 @@ namespace Service { -typedef s32 NativeUID; ///< Native handle for a service - static const int kMaxPortSize = 0x08; ///< Maximum size of a port name (8 characters) static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header @@ -56,11 +54,11 @@ public: }; /** - * Gets the UID for the serice - * @return UID of service in native format + * Gets the Handle for the serice + * @return Handle of service in native format */ - NativeUID GetUID() const { - return (NativeUID)m_uid; + Handle GetHandle() const { + return m_handle; } /** @@ -73,7 +71,7 @@ public: /// Allocates a new handle for the service Handle NewHandle() { - Handle handle = (m_handles.size() << 16) | m_uid; + Handle handle = (m_handles.size() << 16) | m_handle; m_handles.push_back(handle); return handle; } @@ -124,7 +122,7 @@ protected: } private: - u32 m_uid; + u32 m_handle; std::vector m_handles; std::map m_functions; @@ -145,7 +143,7 @@ public: void DeleteService(std::string port_name); /// Get a Service Interface from its UID - Interface* FetchFromUID(u32 uid); + Interface* FetchFromHandle(u32 uid); /// Get a Service Interface from its port Interface* FetchFromPortName(std::string port_name); @@ -153,13 +151,13 @@ public: private: /// Convert an index into m_services vector into a UID - static u32 GetUIDFromIndex(const int index) { + static Handle GetHandleFromIndex(const int index) { return index | 0x10000000; } /// Convert a UID into an index into m_services - static int GetIndexFromUID(const u32 uid) { - return uid & 0x0FFFFFFF; + static int GetIndexFromHandle(const Handle handle) { + return handle & 0x0FFFFFFF; } std::vector m_services; diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index a3d0411767..ff6da8f1cd 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -30,10 +30,10 @@ void GetServiceHandle(Service::Interface* self) { Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); NOTICE_LOG(OSHLE, "SRV::Sync - GetHandle - port: %s, handle: 0x%08X", port_name.c_str(), - service->GetUID()); + service->GetHandle()); if (NULL != service) { - cmd_buff[3] = service->GetUID(); + cmd_buff[3] = service->GetHandle(); } else { ERROR_LOG(OSHLE, "Service %s does not exist", port_name.c_str()); res = -1; diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index 047d8c40fc..1d7daf95c0 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -83,7 +83,7 @@ Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherper /// Connect to an OS service given the port name, returns the handle to the port to out Result ConnectToPort(void* out, const char* port_name) { Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); - Core::g_app_core->SetReg(1, service->GetUID()); + Core::g_app_core->SetReg(1, service->GetHandle()); DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name); return 0; } @@ -91,7 +91,7 @@ Result ConnectToPort(void* out, const char* port_name) { /// Synchronize to an OS service Result SendSyncRequest(Handle session) { DEBUG_LOG(SVC, "SendSyncRequest called session=0x%08X"); - Service::Interface* service = Service::g_manager->FetchFromUID(session); + Service::Interface* service = Service::g_manager->FetchFromHandle(session); service->Sync(); return 0; } From eab6fd01d7d2e9b7434a8c5654d424cb563c3784 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 May 2014 21:43:29 -0400 Subject: [PATCH 36/72] - updated service(s) to be KernelObject's - various cleanups --- src/core/hle/kernel/kernel.h | 9 +++--- src/core/hle/service/apt.h | 2 +- src/core/hle/service/gsp.h | 2 +- src/core/hle/service/hid.h | 2 +- src/core/hle/service/service.cpp | 22 +++++--------- src/core/hle/service/service.h | 51 ++++++++++---------------------- src/core/hle/service/srv.h | 2 +- 7 files changed, 31 insertions(+), 59 deletions(-) diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index d4bb28c728..8f2b7b36da 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -10,10 +10,11 @@ typedef u32 Handle; typedef s32 Result; enum KernelIDType { - KERNEL_ID_TYPE_THREAD = 1, - KERNEL_ID_TYPE_SEMAPHORE = 2, - KERNEL_ID_TYPE_MUTEX = 3, - KERNEL_ID_TYPE_EVENT = 4, + KERNEL_ID_TYPE_THREAD, + KERNEL_ID_TYPE_SEMAPHORE, + KERNEL_ID_TYPE_MUTEX, + KERNEL_ID_TYPE_EVENT, + KERNEL_ID_TYPE_SERVICE, }; enum { diff --git a/src/core/hle/service/apt.h b/src/core/hle/service/apt.h index 4c7dd07e7b..dca3097edb 100644 --- a/src/core/hle/service/apt.h +++ b/src/core/hle/service/apt.h @@ -29,7 +29,7 @@ public: * Gets the string port name used by CTROS for the service * @return Port name of service */ - std::string GetPortName() const { + const char *GetPortName() const { return "APT:U"; } }; diff --git a/src/core/hle/service/gsp.h b/src/core/hle/service/gsp.h index 5ba09ab705..eb5786cd17 100644 --- a/src/core/hle/service/gsp.h +++ b/src/core/hle/service/gsp.h @@ -23,7 +23,7 @@ public: * Gets the string port name used by CTROS for the service * @return Port name of service */ - std::string GetPortName() const { + const char *GetPortName() const { return "gsp::Gpu"; } diff --git a/src/core/hle/service/hid.h b/src/core/hle/service/hid.h index b17fcfa865..81c29eb2eb 100644 --- a/src/core/hle/service/hid.h +++ b/src/core/hle/service/hid.h @@ -25,7 +25,7 @@ public: * Gets the string port name used by CTROS for the service * @return Port name of service */ - std::string GetPortName() const { + const char *GetPortName() const { return "hid:USER"; } diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5601e59a1e..b3e414e0f9 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -7,12 +7,15 @@ #include "common/string_util.h" #include "core/hle/hle.h" + #include "core/hle/service/service.h" #include "core/hle/service/apt.h" #include "core/hle/service/gsp.h" #include "core/hle/service/hid.h" #include "core/hle/service/srv.h" +#include "core/hle/kernel/kernel.h" + namespace Service { Manager* g_manager = NULL; ///< Service manager @@ -31,32 +34,21 @@ Manager::~Manager() { /// Add a service to the manager (does not create it though) void Manager::AddService(Interface* service) { - int index = m_services.size(); - Handle handle = GetHandleFromIndex(index); - + m_port_map[service->GetPortName()] = g_kernel_objects.Create(service); m_services.push_back(service); - - m_port_map[service->GetPortName()] = handle; - service->m_handle = handle; } /// Removes a service from the manager, also frees memory void Manager::DeleteService(std::string port_name) { - auto service = FetchFromPortName(port_name); - - m_services.erase(m_services.begin() + GetIndexFromHandle(service->m_handle)); + Interface* service = FetchFromPortName(port_name); + m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end()); m_port_map.erase(port_name); - delete service; } /// Get a Service Interface from its Handle Interface* Manager::FetchFromHandle(Handle handle) { - int index = GetIndexFromHandle(handle); - if (index < (int)m_services.size()) { - return m_services[index]; - } - return NULL; + return g_kernel_objects.GetFast(handle); } /// Get a Service Interface from its port diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index c3dbd202fc..35735a00be 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -35,15 +36,15 @@ inline static u32* GetCommandBuffer(const int offset=0) { class Manager; /// Interface to a CTROS service -class Interface : NonCopyable { +class Interface : public KernelObject { friend class Manager; public: + + const char *GetName() { return GetPortName(); } + const char *GetTypeName() { return GetPortName(); } - Interface() { - } - - virtual ~Interface() { - } + static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } + KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } typedef void (*Function)(Interface*); @@ -53,37 +54,24 @@ public: std::string name; }; - /** - * Gets the Handle for the serice - * @return Handle of service in native format - */ - Handle GetHandle() const { - return m_handle; - } - /** * Gets the string name used by CTROS for a service * @return Port name of service */ - virtual std::string GetPortName() const { + virtual const char *GetPortName() const { return "[UNKNOWN SERVICE PORT]"; } /// Allocates a new handle for the service Handle NewHandle() { - Handle handle = (m_handles.size() << 16) | m_handle; + Handle handle = (m_handles.size() << 16) | 0;//m_handle; m_handles.push_back(handle); return handle; } /// Frees a handle from the service void DeleteHandle(Handle handle) { - for(auto iter = m_handles.begin(); iter != m_handles.end(); ++iter) { - if(*iter == handle) { - m_handles.erase(iter); - break; - } - } + m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); } /** @@ -96,12 +84,12 @@ public: if (itr == m_functions.end()) { ERROR_LOG(OSHLE, "Unknown/unimplemented function: port = %s, command = 0x%08X!", - GetPortName().c_str(), cmd_buff[0]); + GetPortName(), cmd_buff[0]); return -1; } if (itr->second.func == NULL) { ERROR_LOG(OSHLE, "Unimplemented function: port = %s, name = %s!", - GetPortName().c_str(), itr->second.name.c_str()); + GetPortName(), itr->second.name.c_str()); return -1; } @@ -122,10 +110,10 @@ protected: } private: - u32 m_handle; - + std::vector m_handles; std::map m_functions; + }; /// Simple class to manage accessing services from ports and UID handles @@ -150,18 +138,9 @@ public: private: - /// Convert an index into m_services vector into a UID - static Handle GetHandleFromIndex(const int index) { - return index | 0x10000000; - } - - /// Convert a UID into an index into m_services - static int GetIndexFromHandle(const Handle handle) { - return handle & 0x0FFFFFFF; - } - std::vector m_services; std::map m_port_map; + }; /// Initialize ServiceManager diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/srv.h index f465ebc06f..1e35032ba5 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/srv.h @@ -22,7 +22,7 @@ public: * Gets the string name used by CTROS for the service * @return Port name of service */ - std::string GetPortName() const { + const char *GetPortName() const { return "srv:"; } From 16d55842c561dea86ef79049abb00dafa76b08ef Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 May 2014 21:43:41 -0400 Subject: [PATCH 37/72] fix warning --- src/core/hle/syscall.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index 1d7daf95c0..84c247ae35 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -120,7 +120,7 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronizationN called handle_count=%d, wait_all=%s, nanoseconds=%d %s", handle_count, (wait_all ? "true" : "false"), nano_seconds); - for (int i = 0; i < handle_count; i++) { + for (u32 i = 0; i < handle_count; i++) { DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]); } return 0; From 112904b832210d7b8d165988643acae2f68793e7 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 May 2014 22:09:08 -0400 Subject: [PATCH 38/72] - renamed NewHandle to CreateHandle - updated CreateHandle/DeleteHandle to use KernelObject's --- src/core/hle/service/gsp.cpp | 4 +--- src/core/hle/service/service.h | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/hle/service/gsp.cpp b/src/core/hle/service/gsp.cpp index 12c7dabcd4..50cee2c41b 100644 --- a/src/core/hle/service/gsp.cpp +++ b/src/core/hle/service/gsp.cpp @@ -27,7 +27,7 @@ union GX_CmdBufferHeader { // <=15 when writing a command to shared memory. This is incremented by the application when // writing a command to shared memory, after increasing this value TriggerCmdReqQueue is only // used if this field is value 1. - BitField<8,8,u32> number_commands; + BitField<8,8,u32> number_commands; }; @@ -101,9 +101,7 @@ void RegisterInterruptRelayQueue(Service::Interface* self) { u32* cmd_buff = Service::GetCommandBuffer(); u32 flags = cmd_buff[1]; u32 event_handle = cmd_buff[3]; // TODO(bunnei): Implement event handling - cmd_buff[2] = g_thread_id; // ThreadID - cmd_buff[4] = self->NewHandle(); } /// This triggers handling of the GX command written to the command buffer in shared memory. diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 35735a00be..450a439fe0 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -63,14 +63,16 @@ public: } /// Allocates a new handle for the service - Handle NewHandle() { - Handle handle = (m_handles.size() << 16) | 0;//m_handle; + Handle CreateHandle(KernelObject *obj) { + Handle handle = g_kernel_objects.Create(obj); m_handles.push_back(handle); return handle; } /// Frees a handle from the service - void DeleteHandle(Handle handle) { + template + void DeleteHandle(const Handle handle) { + g_kernel_objects.Destroy(handle); m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); } @@ -111,8 +113,8 @@ protected: private: - std::vector m_handles; - std::map m_functions; + std::vector m_handles; + std::map m_functions; }; From 088a2de9a6f593460dcd532c4a217e0c0fc4fd72 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 18 May 2014 22:24:26 -0400 Subject: [PATCH 39/72] renamed "session" to "handle" --- src/core/hle/syscall.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/hle/syscall.cpp b/src/core/hle/syscall.cpp index 84c247ae35..9a12352468 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/syscall.cpp @@ -89,9 +89,9 @@ Result ConnectToPort(void* out, const char* port_name) { } /// Synchronize to an OS service -Result SendSyncRequest(Handle session) { - DEBUG_LOG(SVC, "SendSyncRequest called session=0x%08X"); - Service::Interface* service = Service::g_manager->FetchFromHandle(session); +Result SendSyncRequest(Handle handle) { + DEBUG_LOG(SVC, "SendSyncRequest called handle=0x%08X"); + Service::Interface* service = Service::g_manager->FetchFromHandle(handle); service->Sync(); return 0; } From 29930ac1101ca12213169d9a21c45efa0d07a374 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 19 May 2014 22:19:10 -0400 Subject: [PATCH 40/72] VFP: disable DBG messages because they spam the console with unimportant skyeye junk --- src/core/arm/interpreter/vfp/vfp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/arm/interpreter/vfp/vfp.h b/src/core/arm/interpreter/vfp/vfp.h index f738a615b1..bbf4caeb02 100644 --- a/src/core/arm/interpreter/vfp/vfp.h +++ b/src/core/arm/interpreter/vfp/vfp.h @@ -21,7 +21,7 @@ #ifndef __VFP_H__ #define __VFP_H__ -#define DBG(...) DEBUG_LOG(ARM11, __VA_ARGS__) +#define DBG(...) //DEBUG_LOG(ARM11, __VA_ARGS__) #define vfpdebug //printf From f5a119a3678fb8bc70a846a166a0993fe4a98c46 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 19 May 2014 22:19:48 -0400 Subject: [PATCH 41/72] thread: added declaration for __KernelReschedule to be used by syscall module --- src/core/hle/kernel/thread.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0d0f465000..eca84c7180 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -28,6 +28,9 @@ Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, /// Sets up the primary application thread Handle __KernelSetupMainThread(s32 priority, int stack_size=KERNEL_DEFAULT_STACK_SIZE); +/// Reschedules to the next available thread (call after current thread is suspended) +void __KernelReschedule(const char *reason); + void __KernelThreadingInit(); void __KernelThreadingShutdown(); From 0886dc70ed3eb3c30fcbe0039d53a5c780a6c4b9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 19 May 2014 22:21:17 -0400 Subject: [PATCH 42/72] apt: changed stubbed handle to be something other than 0xDEADBEEF (used as a magic value in other places) so that I can track how it propagates through the app code --- src/core/hle/service/apt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp index ddb975607c..1f6a70eab9 100644 --- a/src/core/hle/service/apt.cpp +++ b/src/core/hle/service/apt.cpp @@ -23,7 +23,7 @@ void GetLockHandle(Service::Interface* self) { // TODO: This should be an actual mutex handle. Games will check that this is not non-zero // (NULL), and fail if such. A faked non-zero value will at least enable further booting. - cmd_buff[5] = 0xDEADBEEF; + cmd_buff[5] = 0x12345678; } const Interface::FunctionInfo FunctionTable[] = { From 44336329eddd7dbe1f76144e9a1e95e5f76ed372 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:13:25 -0400 Subject: [PATCH 43/72] - created a Kernel namespace - cleaned up Kernel code a bit (moved stuff into namespace, fixed whitespace issues) - added handle types for all different CTROS handles --- src/core/hle/kernel/kernel.cpp | 49 ++++++++++----------- src/core/hle/kernel/kernel.h | 75 +++++++++++++++++++------------- src/core/hle/kernel/thread.cpp | 18 ++++---- src/core/hle/kernel/thread.h | 4 +- src/core/hle/service/service.cpp | 4 +- src/core/hle/service/service.h | 12 ++--- 6 files changed, 87 insertions(+), 75 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index f7145ddd8f..b1fdffde5e 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -12,22 +12,16 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -KernelObjectPool g_kernel_objects; +namespace Kernel { -void __KernelInit() { - __KernelThreadingInit(); -} +ObjectPool g_object_pool; -void __KernelShutdown() { - __KernelThreadingShutdown(); -} - -KernelObjectPool::KernelObjectPool() { +ObjectPool::ObjectPool() { memset(occupied, 0, sizeof(bool) * MAX_COUNT); next_id = INITIAL_NEXT_ID; } -Handle KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_top) { +Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { if (range_top > MAX_COUNT) { range_top = MAX_COUNT; } @@ -46,8 +40,7 @@ Handle KernelObjectPool::Create(KernelObject *obj, int range_bottom, int range_t return 0; } -bool KernelObjectPool::IsValid(Handle handle) -{ +bool ObjectPool::IsValid(Handle handle) { int index = handle - HANDLE_OFFSET; if (index < 0) return false; @@ -57,26 +50,24 @@ bool KernelObjectPool::IsValid(Handle handle) return occupied[index]; } -void KernelObjectPool::Clear() -{ - for (int i = 0; i < MAX_COUNT; i++) - { +void ObjectPool::Clear() { + for (int i = 0; i < MAX_COUNT; i++) { //brutally clear everything, no validation if (occupied[i]) delete pool[i]; occupied[i] = false; } - memset(pool, 0, sizeof(KernelObject*)*MAX_COUNT); + memset(pool, 0, sizeof(Object*)*MAX_COUNT); next_id = INITIAL_NEXT_ID; } -KernelObject *&KernelObjectPool::operator [](Handle handle) +Object* &ObjectPool::operator [](Handle handle) { _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); return pool[handle - HANDLE_OFFSET]; } -void KernelObjectPool::List() { +void ObjectPool::List() { for (int i = 0; i < MAX_COUNT; i++) { if (occupied[i]) { if (pool[i]) { @@ -87,18 +78,16 @@ void KernelObjectPool::List() { } } -int KernelObjectPool::GetCount() -{ +int ObjectPool::GetCount() { int count = 0; - for (int i = 0; i < MAX_COUNT; i++) - { + for (int i = 0; i < MAX_COUNT; i++) { if (occupied[i]) count++; } return count; } -KernelObject *KernelObjectPool::CreateByIDType(int type) { +Object* ObjectPool::CreateByIDType(int type) { // Used for save states. This is ugly, but what other way is there? switch (type) { //case SCE_KERNEL_TMID_Alarm: @@ -142,8 +131,18 @@ KernelObject *KernelObjectPool::CreateByIDType(int type) { } } +void Init() { + __KernelThreadingInit(); +} + +void Shutdown() { + __KernelThreadingShutdown(); +} + +} // namespace + bool __KernelLoadExec(u32 entry_point) { - __KernelInit(); + Kernel::Init(); Core::g_app_core->SetPC(entry_point); diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 8f2b7b36da..19edb7b57f 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -9,41 +9,50 @@ typedef u32 Handle; typedef s32 Result; -enum KernelIDType { - KERNEL_ID_TYPE_THREAD, - KERNEL_ID_TYPE_SEMAPHORE, - KERNEL_ID_TYPE_MUTEX, - KERNEL_ID_TYPE_EVENT, - KERNEL_ID_TYPE_SERVICE, -}; +namespace Kernel { +enum class HandleType : u32 { + Unknown = 0, + Port = 1, + Service = 2, + Event = 3, + Mutex = 4, + SharedMemory = 5, + Redirection = 6, + Thread = 7, + Process = 8, + Arbiter = 9, + File = 10, + Semaphore = 11, +}; + enum { - KERNEL_MAX_NAME_LENGTH = 0x100, - KERNEL_DEFAULT_STACK_SIZE = 0x4000, + MAX_NAME_LENGTH = 0x100, + DEFAULT_STACK_SIZE = 0x4000, }; -class KernelObjectPool; +class ObjectPool; -class KernelObject { - friend class KernelObjectPool; +class Object : NonCopyable { + friend class ObjectPool; u32 handle; public: - virtual ~KernelObject() {} + virtual ~Object() {} Handle GetHandle() const { return handle; } virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; } virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; } - virtual KernelIDType GetIDType() const = 0; + virtual Kernel::HandleType GetHandleType() const = 0; }; -class KernelObjectPool { +class ObjectPool : NonCopyable { public: - KernelObjectPool(); - ~KernelObjectPool() {} + ObjectPool(); + ~ObjectPool() {} // Allocates a handle within the range and inserts the object into the map. - Handle Create(KernelObject *obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); + Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF); - static KernelObject *CreateByIDType(int type); + static Object* CreateByIDType(int type); template u32 Destroy(Handle handle) { @@ -71,7 +80,7 @@ public: // it just acted as a static case and everything worked. This means that we will never // see the Wrong type object error below, but we'll just have to live with that danger. T* t = static_cast(pool[handle - HANDLE_OFFSET]); - if (t == 0 || t->GetIDType() != T::GetStaticIDType()) { + if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) { WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); outError = 0;//T::GetMissingErrorCode(); return 0; @@ -86,17 +95,17 @@ public: T *GetFast(Handle handle) { const Handle realHandle = handle - HANDLE_OFFSET; _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); - return static_cast(pool[realHandle]); + return static_cast(pool[realHandle]); } template - void Iterate(bool func(T *, ArgT), ArgT arg) { + void Iterate(bool func(T*, ArgT), ArgT arg) { int type = T::GetStaticIDType(); for (int i = 0; i < MAX_COUNT; i++) { if (!occupied[i]) continue; - T *t = static_cast(pool[i]); + T* t = static_cast(pool[i]); if (t->GetIDType() == type) { if (!func(t, arg)) break; @@ -104,33 +113,37 @@ public: } } - bool GetIDType(Handle handle, int *type) const { + bool GetIDType(Handle handle, HandleType* type) const { if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || !occupied[handle - HANDLE_OFFSET]) { ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); return false; } - KernelObject *t = pool[handle - HANDLE_OFFSET]; - *type = t->GetIDType(); + Object* t = pool[handle - HANDLE_OFFSET]; + *type = t->GetHandleType(); return true; } - KernelObject *&operator [](Handle handle); + Object* &operator [](Handle handle); void List(); void Clear(); int GetCount(); private: + enum { MAX_COUNT = 0x1000, HANDLE_OFFSET = 0x100, INITIAL_NEXT_ID = 0x10, }; - KernelObject *pool[MAX_COUNT]; - bool occupied[MAX_COUNT]; - int next_id; + + Object* pool[MAX_COUNT]; + bool occupied[MAX_COUNT]; + int next_id; }; -extern KernelObjectPool g_kernel_objects; +extern ObjectPool g_object_pool; + +} // namespace bool __KernelLoadExec(u32 entry_point); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 634218e8ba..2955d6f5b2 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -42,14 +42,14 @@ enum WaitType { WAITTYPE_SYNCH, }; -class Thread : public KernelObject { +class Thread : public Kernel::Object { public: const char *GetName() { return name; } const char *GetTypeName() { return "Thread"; } - static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } - KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } + static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } + Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; } @@ -71,7 +71,7 @@ public: WaitType wait_type; - char name[KERNEL_MAX_NAME_LENGTH+1]; + char name[Kernel::MAX_NAME_LENGTH + 1]; }; // Lists all thread ids that aren't deleted/etc. @@ -201,7 +201,7 @@ Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, Thread *t = new Thread; - handle = g_kernel_objects.Create(t); + handle = Kernel::g_object_pool.Create(t); g_thread_queue.push_back(handle); g_thread_ready_queue.prepare(priority); @@ -214,8 +214,8 @@ Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, t->processor_id = processor_id; t->wait_type = WAITTYPE_NONE; - strncpy(t->name, name, KERNEL_MAX_NAME_LENGTH); - t->name[KERNEL_MAX_NAME_LENGTH] = '\0'; + strncpy(t->name, name, Kernel::MAX_NAME_LENGTH); + t->name[Kernel::MAX_NAME_LENGTH] = '\0'; return t; } @@ -296,7 +296,7 @@ Thread *__KernelNextThread() { if (next < 0) { return NULL; } - return g_kernel_objects.GetFast(next); + return Kernel::g_object_pool.GetFast(next); } /// Sets up the primary application thread @@ -326,7 +326,7 @@ Handle __KernelSetupMainThread(s32 priority, int stack_size) { /// Resumes a thread from waiting by marking it as "ready" void __KernelResumeThreadFromWait(Handle handle) { u32 error; - Thread *t = g_kernel_objects.Get(handle, error); + Thread *t = Kernel::g_object_pool.Get(handle, error); if (t) { t->status &= ~THREADSTATUS_WAIT; if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index eca84c7180..207a5227b1 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -23,10 +23,10 @@ enum ThreadProcessorId { /// Creates a new thread - wrapper for external user Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, - s32 processor_id, u32 stack_top, int stack_size=KERNEL_DEFAULT_STACK_SIZE); + s32 processor_id, u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Sets up the primary application thread -Handle __KernelSetupMainThread(s32 priority, int stack_size=KERNEL_DEFAULT_STACK_SIZE); +Handle __KernelSetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Reschedules to the next available thread (call after current thread is suspended) void __KernelReschedule(const char *reason); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index b3e414e0f9..08d0c43ff5 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -34,7 +34,7 @@ Manager::~Manager() { /// Add a service to the manager (does not create it though) void Manager::AddService(Interface* service) { - m_port_map[service->GetPortName()] = g_kernel_objects.Create(service); + m_port_map[service->GetPortName()] = Kernel::g_object_pool.Create(service); m_services.push_back(service); } @@ -48,7 +48,7 @@ void Manager::DeleteService(std::string port_name) { /// Get a Service Interface from its Handle Interface* Manager::FetchFromHandle(Handle handle) { - return g_kernel_objects.GetFast(handle); + return Kernel::g_object_pool.GetFast(handle); } /// Get a Service Interface from its port diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 450a439fe0..f334dbcb8e 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -36,15 +36,15 @@ inline static u32* GetCommandBuffer(const int offset=0) { class Manager; /// Interface to a CTROS service -class Interface : public KernelObject { +class Interface : public Kernel::Object { friend class Manager; public: const char *GetName() { return GetPortName(); } const char *GetTypeName() { return GetPortName(); } - static KernelIDType GetStaticIDType() { return KERNEL_ID_TYPE_THREAD; } - KernelIDType GetIDType() const { return KERNEL_ID_TYPE_THREAD; } + static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Service; } + Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Service; } typedef void (*Function)(Interface*); @@ -63,8 +63,8 @@ public: } /// Allocates a new handle for the service - Handle CreateHandle(KernelObject *obj) { - Handle handle = g_kernel_objects.Create(obj); + Handle CreateHandle(Kernel::Object *obj) { + Handle handle = Kernel::g_object_pool.Create(obj); m_handles.push_back(handle); return handle; } @@ -72,7 +72,7 @@ public: /// Frees a handle from the service template void DeleteHandle(const Handle handle) { - g_kernel_objects.Destroy(handle); + g_object_pool.Destroy(handle); m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); } From f654a03f1f23bea8fe5bf7194614ce6e947d0c5c Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:20:35 -0400 Subject: [PATCH 44/72] thread: whitespace change - fixed * and & placement --- src/core/hle/kernel/thread.cpp | 50 +++++++++++++++++----------------- src/core/hle/kernel/thread.h | 4 +-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 2955d6f5b2..cfc5327a3e 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -45,8 +45,8 @@ enum WaitType { class Thread : public Kernel::Object { public: - const char *GetName() { return name; } - const char *GetTypeName() { return "Thread"; } + const char* GetName() { return name; } + const char* GetTypeName() { return "Thread"; } static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } @@ -85,11 +85,11 @@ Handle g_current_thread_handle; Thread* g_current_thread; -inline Thread *__GetCurrentThread() { +inline Thread* __GetCurrentThread() { return g_current_thread; } -inline void __SetCurrentThread(Thread *t) { +inline void __SetCurrentThread(Thread* t) { g_current_thread = t; g_current_thread_handle = t->GetHandle(); } @@ -97,7 +97,7 @@ inline void __SetCurrentThread(Thread *t) { //////////////////////////////////////////////////////////////////////////////////////////////////// /// Saves the current CPU context -void __KernelSaveContext(ThreadContext &ctx) { +void __KernelSaveContext(ThreadContext& ctx) { ctx.cpu_registers[0] = Core::g_app_core->GetReg(0); ctx.cpu_registers[1] = Core::g_app_core->GetReg(1); ctx.cpu_registers[2] = Core::g_app_core->GetReg(2); @@ -118,7 +118,7 @@ void __KernelSaveContext(ThreadContext &ctx) { } /// Loads a CPU context -void __KernelLoadContext(const ThreadContext &ctx) { +void __KernelLoadContext(const ThreadContext& ctx) { Core::g_app_core->SetReg(0, ctx.cpu_registers[0]); Core::g_app_core->SetReg(1, ctx.cpu_registers[1]); Core::g_app_core->SetReg(2, ctx.cpu_registers[2]); @@ -141,7 +141,7 @@ void __KernelLoadContext(const ThreadContext &ctx) { } /// Resets a thread -void __KernelResetThread(Thread *t, s32 lowest_priority) { +void __KernelResetThread(Thread* t, s32 lowest_priority) { memset(&t->context, 0, sizeof(ThreadContext)); t->context.pc = t->entry_point; @@ -155,7 +155,7 @@ void __KernelResetThread(Thread *t, s32 lowest_priority) { } /// Change a thread to "ready" state -void __KernelChangeReadyState(Thread *t, bool ready) { +void __KernelChangeReadyState(Thread* t, bool ready) { Handle handle = t->GetHandle(); if (t->IsReady()) { if (!ready) { @@ -172,7 +172,7 @@ void __KernelChangeReadyState(Thread *t, bool ready) { } /// Changes a threads state -void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) { +void __KernelChangeThreadState(Thread* t, ThreadStatus new_status) { if (!t || t->status == new_status) { return; } @@ -187,7 +187,7 @@ void __KernelChangeThreadState(Thread *t, ThreadStatus new_status) { } /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void __KernelCallThread(Thread *t) { +void __KernelCallThread(Thread* t) { // Stop waiting if (t->wait_type != WAITTYPE_NONE) { t->wait_type = WAITTYPE_NONE; @@ -196,10 +196,10 @@ void __KernelCallThread(Thread *t) { } /// Creates a new thread -Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, s32 priority, +Thread* __KernelCreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { - Thread *t = new Thread; + Thread* t = new Thread; handle = Kernel::g_object_pool.Create(t); @@ -221,7 +221,7 @@ Thread *__KernelCreateThread(Handle &handle, const char *name, u32 entry_point, } /// Creates a new thread - wrapper for external user -Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 processor_id, +Handle __KernelCreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { if (name == NULL) { ERROR_LOG(KERNEL, "__KernelCreateThread(): NULL name"); @@ -245,7 +245,7 @@ Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 return -1; } Handle handle; - Thread *t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top, + Thread* t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); HLE::EatCycles(32000); @@ -260,8 +260,8 @@ Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, s32 } /// Switches CPU context to that of the specified thread -void __KernelSwitchContext(Thread* t, const char *reason) { - Thread *cur = __GetCurrentThread(); +void __KernelSwitchContext(Thread* t, const char* reason) { + Thread* cur = __GetCurrentThread(); // Save context for current thread if (cur) { @@ -284,9 +284,9 @@ void __KernelSwitchContext(Thread* t, const char *reason) { } /// Gets the next thread that is ready to be run by priority -Thread *__KernelNextThread() { +Thread* __KernelNextThread() { Handle next; - Thread *cur = __GetCurrentThread(); + Thread* cur = __GetCurrentThread(); if (cur && cur->IsRunning()) { next = g_thread_ready_queue.pop_first_better(cur->current_priority); @@ -304,13 +304,13 @@ Handle __KernelSetupMainThread(s32 priority, int stack_size) { Handle handle; // Initialize new "main" thread - Thread *t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, + Thread* t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); __KernelResetThread(t, 0); // If running another thread already, set it to "ready" state - Thread *cur = __GetCurrentThread(); + Thread* cur = __GetCurrentThread(); if (cur && cur->IsRunning()) { __KernelChangeReadyState(cur, true); } @@ -326,7 +326,7 @@ Handle __KernelSetupMainThread(s32 priority, int stack_size) { /// Resumes a thread from waiting by marking it as "ready" void __KernelResumeThreadFromWait(Handle handle) { u32 error; - Thread *t = Kernel::g_object_pool.Get(handle, error); + Thread* t = Kernel::g_object_pool.Get(handle, error); if (t) { t->status &= ~THREADSTATUS_WAIT; if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { @@ -336,15 +336,15 @@ void __KernelResumeThreadFromWait(Handle handle) { } /// Puts a thread in the wait state for the given type/reason -void __KernelWaitCurThread(WaitType wait_type, const char *reason) { - Thread *t = __GetCurrentThread(); +void __KernelWaitCurThread(WaitType wait_type, const char* reason) { + Thread* t = __GetCurrentThread(); t->wait_type = wait_type; __KernelChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); } /// Reschedules to the next available thread (call after current thread is suspended) -void __KernelReschedule(const char *reason) { - Thread *next = __KernelNextThread(); +void __KernelReschedule(const char* reason) { + Thread* next = __KernelNextThread(); if (next > 0) { __KernelSwitchContext(next, reason); } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 207a5227b1..72e9a416de 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -22,14 +22,14 @@ enum ThreadProcessorId { }; /// Creates a new thread - wrapper for external user -Handle __KernelCreateThread(const char *name, u32 entry_point, s32 priority, +Handle __KernelCreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Sets up the primary application thread Handle __KernelSetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Reschedules to the next available thread (call after current thread is suspended) -void __KernelReschedule(const char *reason); +void __KernelReschedule(const char* reason); void __KernelThreadingInit(); void __KernelThreadingShutdown(); From 143bba20453036f0a4bcc74dad10d99605a84732 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:28:38 -0400 Subject: [PATCH 45/72] renamed "syscall" module to "svc" (more accurate naming) --- src/core/CMakeLists.txt | 2 +- src/core/core.vcxproj | 4 ++-- src/core/core.vcxproj.filters | 12 ++++++------ src/core/hle/hle.cpp | 14 +++++++------- src/core/hle/hle.h | 2 +- src/core/hle/kernel/thread.cpp | 2 +- src/core/hle/service/service.h | 2 +- src/core/hle/{syscall.cpp => svc.cpp} | 12 +++++++----- src/core/hle/{syscall.h => svc.h} | 4 ++-- 9 files changed, 28 insertions(+), 26 deletions(-) rename src/core/hle/{syscall.cpp => svc.cpp} (98%) rename src/core/hle/{syscall.h => svc.h} (95%) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b0597db385..e006502da5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -33,7 +33,7 @@ set(SRCS core.cpp hle/hle.cpp hle/config_mem.cpp hle/coprocessor.cpp - hle/syscall.cpp + hle/svc.cpp hle/kernel/kernel.cpp hle/kernel/thread.cpp hle/service/apt.cpp diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index f077154ee1..6eb58a636c 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -175,7 +175,7 @@ - + @@ -223,7 +223,7 @@ - + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 6aedeb54b0..fc4e35edb0 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -84,9 +84,6 @@ hle - - hle - hle\service @@ -162,6 +159,9 @@ hle\kernel + + hle + @@ -226,9 +226,6 @@ hle\service - - hle - hle\service @@ -289,6 +286,9 @@ hle\kernel + + hle + diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index 4523845711..080c36abf4 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -6,7 +6,7 @@ #include "core/mem_map.h" #include "core/hle/hle.h" -#include "core/hle/syscall.h" +#include "core/hle/svc.h" #include "core/hle/service/service.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,17 +15,17 @@ namespace HLE { static std::vector g_module_db; -const FunctionDef* GetSyscallInfo(u32 opcode) { +const FunctionDef* GetSVCInfo(u32 opcode) { u32 func_num = opcode & 0xFFFFFF; // 8 bits if (func_num > 0xFF) { - ERROR_LOG(HLE,"Unknown syscall: 0x%02X", func_num); + ERROR_LOG(HLE,"Unknown SVC: 0x%02X", func_num); return NULL; } return &g_module_db[0].func_table[func_num]; } -void CallSyscall(u32 opcode) { - const FunctionDef *info = GetSyscallInfo(opcode); +void CallSVC(u32 opcode) { + const FunctionDef *info = GetSVCInfo(opcode); if (!info) { return; @@ -33,7 +33,7 @@ void CallSyscall(u32 opcode) { if (info->func) { info->func(); } else { - ERROR_LOG(HLE, "Unimplemented SysCall function %s(..)", info->name.c_str()); + ERROR_LOG(HLE, "Unimplemented SVC function %s(..)", info->name.c_str()); } } @@ -54,7 +54,7 @@ void RegisterModule(std::string name, int num_functions, const FunctionDef* func } void RegisterAllModules() { - Syscall::Register(); + SVC::Register(); } void Init() { diff --git a/src/core/hle/hle.h b/src/core/hle/hle.h index 452546e1f8..c075147c39 100644 --- a/src/core/hle/hle.h +++ b/src/core/hle/hle.h @@ -34,7 +34,7 @@ struct ModuleDef { void RegisterModule(std::string name, int num_functions, const FunctionDef *func_table); -void CallSyscall(u32 opcode); +void CallSVC(u32 opcode); void EatCycles(u32 cycles); diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index cfc5327a3e..136fff0211 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -15,7 +15,7 @@ #include "core/core.h" #include "core/mem_map.h" #include "core/hle/hle.h" -#include "core/hle/syscall.h" +#include "core/hle/svc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index f334dbcb8e..eba730efb9 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -14,7 +14,7 @@ #include "core/mem_map.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/syscall.h" +#include "core/hle/svc.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service diff --git a/src/core/hle/syscall.cpp b/src/core/hle/svc.cpp similarity index 98% rename from src/core/hle/syscall.cpp rename to src/core/hle/svc.cpp index 9a12352468..a9141699c8 100644 --- a/src/core/hle/syscall.cpp +++ b/src/core/hle/svc.cpp @@ -13,14 +13,14 @@ #include "core/hle/kernel/thread.h" #include "core/hle/function_wrappers.h" -#include "core/hle/syscall.h" +#include "core/hle/svc.h" #include "core/hle/service/service.h" #include "core/hle/kernel/thread.h" //////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace Syscall +// Namespace SVC -namespace Syscall { +namespace SVC { enum ControlMemoryOperation { MEMORY_OPERATION_HEAP = 0x00000003, @@ -123,6 +123,8 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa for (u32 i = 0; i < handle_count; i++) { DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]); } + __KernelReschedule("WaitSynchronizationN"); + return 0; } @@ -212,7 +214,7 @@ Result CreateEvent(void* _event, u32 reset_type) { return 0; } -const HLE::FunctionDef Syscall_Table[] = { +const HLE::FunctionDef SVC_Table[] = { {0x00, NULL, "Unknown"}, {0x01, WrapI_VUUUUU, "ControlMemory"}, {0x02, WrapI_VVU, "QueryMemory"}, @@ -342,7 +344,7 @@ const HLE::FunctionDef Syscall_Table[] = { }; void Register() { - HLE::RegisterModule("SyscallTable", ARRAY_SIZE(Syscall_Table), Syscall_Table); + HLE::RegisterModule("SVC_Table", ARRAY_SIZE(SVC_Table), SVC_Table); } } // namespace diff --git a/src/core/hle/syscall.h b/src/core/hle/svc.h similarity index 95% rename from src/core/hle/syscall.h rename to src/core/hle/svc.h index 3da349ed5f..5c35977d13 100644 --- a/src/core/hle/syscall.h +++ b/src/core/hle/svc.h @@ -39,9 +39,9 @@ enum ResetType { }; //////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace Syscall +// Namespace SVC -namespace Syscall { +namespace SVC { void Register(); From 49dc2ce8ac4fc37a008fa28e0771c8c74c576b05 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:50:16 -0400 Subject: [PATCH 46/72] ARM_Interface: added SaveContext and LoadContext functions for HLE thread switching --- src/core/arm/arm_interface.h | 16 ++++++++- src/core/arm/interpreter/arm_interpreter.cpp | 36 +++++++++++++++++++ src/core/arm/interpreter/arm_interpreter.h | 12 +++++++ src/core/hle/kernel/thread.cpp | 38 ++------------------ 4 files changed, 65 insertions(+), 37 deletions(-) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 5c382ebbdd..52bc82115f 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -7,6 +7,8 @@ #include "common/common.h" #include "common/common_types.h" +#include "core/hle/svc.h" + /// Generic ARM11 CPU interface class ARM_Interface : NonCopyable { public: @@ -75,6 +77,18 @@ public: */ virtual u64 GetTicks() const = 0; + /** + * Saves the current CPU context + * @param ctx Thread context to save + */ + virtual void SaveContext(ThreadContext& ctx) = 0; + + /** + * Loads a CPU context + * @param ctx Thread context to load + */ + virtual void LoadContext(const ThreadContext& ctx) = 0; + /// Getter for m_num_instructions u64 GetNumInstructions() { return m_num_instructions; @@ -90,6 +104,6 @@ protected: private: - u64 m_num_instructions; ///< Number of instructions executed + u64 m_num_instructions; ///< Number of instructions executed }; diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp index c21ff04649..b8c46cdfca 100644 --- a/src/core/arm/interpreter/arm_interpreter.cpp +++ b/src/core/arm/interpreter/arm_interpreter.cpp @@ -101,3 +101,39 @@ void ARM_Interpreter::ExecuteInstructions(int num_instructions) { m_state->NumInstrsToExecute = num_instructions; ARMul_Emulate32(m_state); } + +/** + * Saves the current CPU context + * @param ctx Thread context to save + * @todo Do we need to save Reg[15] and NextInstr? + */ +void ARM_Interpreter::SaveContext(ThreadContext& ctx) { + memcpy(ctx.cpu_registers, m_state->Reg, sizeof(ctx.cpu_registers)); + memcpy(ctx.fpu_registers, m_state->ExtReg, sizeof(ctx.fpu_registers)); + + ctx.sp = m_state->Reg[13]; + ctx.lr = m_state->Reg[14]; + ctx.pc = m_state->pc; + ctx.cpsr = m_state->Cpsr; + + ctx.fpscr = m_state->VFP[1]; + ctx.fpexc = m_state->VFP[2]; +} + +/** + * Loads a CPU context + * @param ctx Thread context to load + * @param Do we need to load Reg[15] and NextInstr? + */ +void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { + memcpy(m_state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); + memcpy(m_state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); + + m_state->Reg[13] = ctx.sp; + m_state->Reg[14] = ctx.lr; + m_state->pc = ctx.pc; + m_state->Cpsr = ctx.cpsr; + + m_state->VFP[1] = ctx.fpscr; + m_state->VFP[2] = ctx.fpexc; +} diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h index 474ba3e450..15240568c2 100644 --- a/src/core/arm/interpreter/arm_interpreter.h +++ b/src/core/arm/interpreter/arm_interpreter.h @@ -60,6 +60,18 @@ public: */ u64 GetTicks() const; + /** + * Saves the current CPU context + * @param ctx Thread context to save + */ + void SaveContext(ThreadContext& ctx); + + /** + * Loads a CPU context + * @param ctx Thread context to load + */ + void LoadContext(const ThreadContext& ctx); + protected: /** diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 136fff0211..b3d306c531 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -98,46 +98,12 @@ inline void __SetCurrentThread(Thread* t) { /// Saves the current CPU context void __KernelSaveContext(ThreadContext& ctx) { - ctx.cpu_registers[0] = Core::g_app_core->GetReg(0); - ctx.cpu_registers[1] = Core::g_app_core->GetReg(1); - ctx.cpu_registers[2] = Core::g_app_core->GetReg(2); - ctx.cpu_registers[3] = Core::g_app_core->GetReg(3); - ctx.cpu_registers[4] = Core::g_app_core->GetReg(4); - ctx.cpu_registers[5] = Core::g_app_core->GetReg(5); - ctx.cpu_registers[6] = Core::g_app_core->GetReg(6); - ctx.cpu_registers[7] = Core::g_app_core->GetReg(7); - ctx.cpu_registers[8] = Core::g_app_core->GetReg(8); - ctx.cpu_registers[9] = Core::g_app_core->GetReg(9); - ctx.cpu_registers[10] = Core::g_app_core->GetReg(10); - ctx.cpu_registers[11] = Core::g_app_core->GetReg(11); - ctx.cpu_registers[12] = Core::g_app_core->GetReg(12); - ctx.sp = Core::g_app_core->GetReg(13); - ctx.lr = Core::g_app_core->GetReg(14); - ctx.pc = Core::g_app_core->GetPC(); - ctx.cpsr = Core::g_app_core->GetCPSR(); + Core::g_app_core->SaveContext(ctx); } /// Loads a CPU context void __KernelLoadContext(const ThreadContext& ctx) { - Core::g_app_core->SetReg(0, ctx.cpu_registers[0]); - Core::g_app_core->SetReg(1, ctx.cpu_registers[1]); - Core::g_app_core->SetReg(2, ctx.cpu_registers[2]); - Core::g_app_core->SetReg(3, ctx.cpu_registers[3]); - Core::g_app_core->SetReg(4, ctx.cpu_registers[4]); - Core::g_app_core->SetReg(5, ctx.cpu_registers[5]); - Core::g_app_core->SetReg(6, ctx.cpu_registers[6]); - Core::g_app_core->SetReg(7, ctx.cpu_registers[7]); - Core::g_app_core->SetReg(8, ctx.cpu_registers[8]); - Core::g_app_core->SetReg(9, ctx.cpu_registers[9]); - Core::g_app_core->SetReg(10, ctx.cpu_registers[10]); - Core::g_app_core->SetReg(11, ctx.cpu_registers[11]); - Core::g_app_core->SetReg(12, ctx.cpu_registers[12]); - Core::g_app_core->SetReg(13, ctx.sp); - Core::g_app_core->SetReg(14, ctx.lr); - //Core::g_app_core->SetReg(15, ctx.pc); - - Core::g_app_core->SetPC(ctx.pc); - Core::g_app_core->SetCPSR(ctx.cpsr); + Core::g_app_core->LoadContext(ctx); } /// Resets a thread From 001280245685ade50326301409e8aee28602504d Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 18:52:54 -0400 Subject: [PATCH 47/72] ARM_Interpreter/ARM_Interface: Fixed member variable naming to be consistent with style guide --- src/core/arm/arm_interface.h | 10 +-- src/core/arm/interpreter/arm_interpreter.cpp | 78 ++++++++++---------- src/core/arm/interpreter/arm_interpreter.h | 2 +- 3 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 52bc82115f..b73786ccd4 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -13,7 +13,7 @@ class ARM_Interface : NonCopyable { public: ARM_Interface() { - m_num_instructions = 0; + num_instructions = 0; } ~ARM_Interface() { @@ -25,7 +25,7 @@ public: */ void Run(int num_instructions) { ExecuteInstructions(num_instructions); - m_num_instructions += num_instructions; + num_instructions += num_instructions; } /// Step CPU by one instruction @@ -89,9 +89,9 @@ public: */ virtual void LoadContext(const ThreadContext& ctx) = 0; - /// Getter for m_num_instructions + /// Getter for num_instructions u64 GetNumInstructions() { - return m_num_instructions; + return num_instructions; } protected: @@ -104,6 +104,6 @@ protected: private: - u64 m_num_instructions; ///< Number of instructions executed + u64 num_instructions; ///< Number of instructions executed }; diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp index b8c46cdfca..a9ec948207 100644 --- a/src/core/arm/interpreter/arm_interpreter.cpp +++ b/src/core/arm/interpreter/arm_interpreter.cpp @@ -9,30 +9,30 @@ const static cpu_config_t s_arm11_cpu_info = { }; ARM_Interpreter::ARM_Interpreter() { - m_state = new ARMul_State; + state = new ARMul_State; ARMul_EmulateInit(); - ARMul_NewState(m_state); + ARMul_NewState(state); - m_state->abort_model = 0; - m_state->cpu = (cpu_config_t*)&s_arm11_cpu_info; - m_state->bigendSig = LOW; + state->abort_model = 0; + state->cpu = (cpu_config_t*)&s_arm11_cpu_info; + state->bigendSig = LOW; - ARMul_SelectProcessor(m_state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); - m_state->lateabtSig = LOW; - mmu_init(m_state); + ARMul_SelectProcessor(state, ARM_v6_Prop | ARM_v5_Prop | ARM_v5e_Prop); + state->lateabtSig = LOW; + mmu_init(state); // Reset the core to initial state - ARMul_Reset(m_state); - m_state->NextInstr = 0; - m_state->Emulate = 3; + ARMul_Reset(state); + state->NextInstr = 0; + state->Emulate = 3; - m_state->pc = m_state->Reg[15] = 0x00000000; - m_state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack + state->pc = state->Reg[15] = 0x00000000; + state->Reg[13] = 0x10000000; // Set stack pointer to the top of the stack } ARM_Interpreter::~ARM_Interpreter() { - delete m_state; + delete state; } /** @@ -40,7 +40,7 @@ ARM_Interpreter::~ARM_Interpreter() { * @param addr Address to set PC to */ void ARM_Interpreter::SetPC(u32 pc) { - m_state->pc = m_state->Reg[15] = pc; + state->pc = state->Reg[15] = pc; } /* @@ -48,7 +48,7 @@ void ARM_Interpreter::SetPC(u32 pc) { * @return Returns current PC */ u32 ARM_Interpreter::GetPC() const { - return m_state->pc; + return state->pc; } /** @@ -57,7 +57,7 @@ u32 ARM_Interpreter::GetPC() const { * @return Returns the value in the register */ u32 ARM_Interpreter::GetReg(int index) const { - return m_state->Reg[index]; + return state->Reg[index]; } /** @@ -66,7 +66,7 @@ u32 ARM_Interpreter::GetReg(int index) const { * @param value Value to set register to */ void ARM_Interpreter::SetReg(int index, u32 value) { - m_state->Reg[index] = value; + state->Reg[index] = value; } /** @@ -74,7 +74,7 @@ void ARM_Interpreter::SetReg(int index, u32 value) { * @return Returns the value of the CPSR register */ u32 ARM_Interpreter::GetCPSR() const { - return m_state->Cpsr; + return state->Cpsr; } /** @@ -82,7 +82,7 @@ u32 ARM_Interpreter::GetCPSR() const { * @param cpsr Value to set CPSR to */ void ARM_Interpreter::SetCPSR(u32 cpsr) { - m_state->Cpsr = cpsr; + state->Cpsr = cpsr; } /** @@ -90,7 +90,7 @@ void ARM_Interpreter::SetCPSR(u32 cpsr) { * @return Returns number of clock ticks */ u64 ARM_Interpreter::GetTicks() const { - return ARMul_Time(m_state); + return ARMul_Time(state); } /** @@ -98,8 +98,8 @@ u64 ARM_Interpreter::GetTicks() const { * @param num_instructions Number of instructions to executes */ void ARM_Interpreter::ExecuteInstructions(int num_instructions) { - m_state->NumInstrsToExecute = num_instructions; - ARMul_Emulate32(m_state); + state->NumInstrsToExecute = num_instructions; + ARMul_Emulate32(state); } /** @@ -108,16 +108,16 @@ void ARM_Interpreter::ExecuteInstructions(int num_instructions) { * @todo Do we need to save Reg[15] and NextInstr? */ void ARM_Interpreter::SaveContext(ThreadContext& ctx) { - memcpy(ctx.cpu_registers, m_state->Reg, sizeof(ctx.cpu_registers)); - memcpy(ctx.fpu_registers, m_state->ExtReg, sizeof(ctx.fpu_registers)); + memcpy(ctx.cpu_registers, state->Reg, sizeof(ctx.cpu_registers)); + memcpy(ctx.fpu_registers, state->ExtReg, sizeof(ctx.fpu_registers)); - ctx.sp = m_state->Reg[13]; - ctx.lr = m_state->Reg[14]; - ctx.pc = m_state->pc; - ctx.cpsr = m_state->Cpsr; + ctx.sp = state->Reg[13]; + ctx.lr = state->Reg[14]; + ctx.pc = state->pc; + ctx.cpsr = state->Cpsr; - ctx.fpscr = m_state->VFP[1]; - ctx.fpexc = m_state->VFP[2]; + ctx.fpscr = state->VFP[1]; + ctx.fpexc = state->VFP[2]; } /** @@ -126,14 +126,14 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) { * @param Do we need to load Reg[15] and NextInstr? */ void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { - memcpy(m_state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); - memcpy(m_state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); + memcpy(state->Reg, ctx.cpu_registers, sizeof(ctx.cpu_registers)); + memcpy(state->ExtReg, ctx.fpu_registers, sizeof(ctx.fpu_registers)); - m_state->Reg[13] = ctx.sp; - m_state->Reg[14] = ctx.lr; - m_state->pc = ctx.pc; - m_state->Cpsr = ctx.cpsr; + state->Reg[13] = ctx.sp; + state->Reg[14] = ctx.lr; + state->pc = ctx.pc; + state->Cpsr = ctx.cpsr; - m_state->VFP[1] = ctx.fpscr; - m_state->VFP[2] = ctx.fpexc; + state->VFP[1] = ctx.fpscr; + state->VFP[2] = ctx.fpexc; } diff --git a/src/core/arm/interpreter/arm_interpreter.h b/src/core/arm/interpreter/arm_interpreter.h index 15240568c2..6a531e4979 100644 --- a/src/core/arm/interpreter/arm_interpreter.h +++ b/src/core/arm/interpreter/arm_interpreter.h @@ -82,6 +82,6 @@ protected: private: - ARMul_State* m_state; + ARMul_State* state; }; From 75c6d2a8fa3547946227094af6c179e5ccba0e1e Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 19:37:46 -0400 Subject: [PATCH 48/72] thread: moved threading calls to the Kernel namespace --- src/core/hle/kernel/kernel.cpp | 6 +- src/core/hle/kernel/thread.cpp | 184 +++++++++++++++++---------------- src/core/hle/kernel/thread.h | 26 +++-- src/core/hle/svc.cpp | 4 +- 4 files changed, 117 insertions(+), 103 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index b1fdffde5e..45e36173c0 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -132,11 +132,11 @@ Object* ObjectPool::CreateByIDType(int type) { } void Init() { - __KernelThreadingInit(); + Kernel::ThreadingInit(); } void Shutdown() { - __KernelThreadingShutdown(); + Kernel::ThreadingShutdown(); } } // namespace @@ -147,7 +147,7 @@ bool __KernelLoadExec(u32 entry_point) { Core::g_app_core->SetPC(entry_point); // 0x30 is the typical main thread priority I've seen used so far - Handle thread_id = __KernelSetupMainThread(0x30); + Handle thread_id = Kernel::SetupMainThread(0x30); return true; } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b3d306c531..7b4f0ea47d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -19,7 +19,7 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -// Enums +namespace Kernel { enum ThreadStatus { THREADSTATUS_RUNNING = 1, @@ -81,33 +81,32 @@ std::vector g_thread_queue; Common::ThreadQueueList g_thread_ready_queue; Handle g_current_thread_handle; - Thread* g_current_thread; +/// Gets the current thread inline Thread* __GetCurrentThread() { return g_current_thread; } +/// Sets the current thread inline void __SetCurrentThread(Thread* t) { g_current_thread = t; g_current_thread_handle = t->GetHandle(); } -//////////////////////////////////////////////////////////////////////////////////////////////////// - /// Saves the current CPU context -void __KernelSaveContext(ThreadContext& ctx) { +void __SaveContext(ThreadContext& ctx) { Core::g_app_core->SaveContext(ctx); } /// Loads a CPU context -void __KernelLoadContext(const ThreadContext& ctx) { +void __LoadContext(const ThreadContext& ctx) { Core::g_app_core->LoadContext(ctx); } /// Resets a thread -void __KernelResetThread(Thread* t, s32 lowest_priority) { +void __ResetThread(Thread* t, s32 lowest_priority) { memset(&t->context, 0, sizeof(ThreadContext)); t->context.pc = t->entry_point; @@ -121,7 +120,7 @@ void __KernelResetThread(Thread* t, s32 lowest_priority) { } /// Change a thread to "ready" state -void __KernelChangeReadyState(Thread* t, bool ready) { +void __ChangeReadyState(Thread* t, bool ready) { Handle handle = t->GetHandle(); if (t->IsReady()) { if (!ready) { @@ -138,11 +137,11 @@ void __KernelChangeReadyState(Thread* t, bool ready) { } /// Changes a threads state -void __KernelChangeThreadState(Thread* t, ThreadStatus new_status) { +void __ChangeThreadState(Thread* t, ThreadStatus new_status) { if (!t || t->status == new_status) { return; } - __KernelChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); + __ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; if (new_status == THREADSTATUS_WAIT) { @@ -153,16 +152,75 @@ void __KernelChangeThreadState(Thread* t, ThreadStatus new_status) { } /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void __KernelCallThread(Thread* t) { +void __CallThread(Thread* t) { // Stop waiting if (t->wait_type != WAITTYPE_NONE) { t->wait_type = WAITTYPE_NONE; } - __KernelChangeThreadState(t, THREADSTATUS_READY); + __ChangeThreadState(t, THREADSTATUS_READY); +} + +/// Switches CPU context to that of the specified thread +void __SwitchContext(Thread* t, const char* reason) { + Thread* cur = __GetCurrentThread(); + + // Save context for current thread + if (cur) { + __SaveContext(cur->context); + + if (cur->IsRunning()) { + __ChangeReadyState(cur, true); + } + } + // Load context of new thread + if (t) { + __SetCurrentThread(t); + __ChangeReadyState(t, false); + t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; + t->wait_type = WAITTYPE_NONE; + __LoadContext(t->context); + } else { + __SetCurrentThread(NULL); + } +} + +/// Gets the next thread that is ready to be run by priority +Thread* __NextThread() { + Handle next; + Thread* cur = __GetCurrentThread(); + + if (cur && cur->IsRunning()) { + next = g_thread_ready_queue.pop_first_better(cur->current_priority); + } else { + next = g_thread_ready_queue.pop_first(); + } + if (next < 0) { + return NULL; + } + return Kernel::g_object_pool.GetFast(next); +} + +/// Resumes a thread from waiting by marking it as "ready" +void __ResumeThreadFromWait(Handle handle) { + u32 error; + Thread* t = Kernel::g_object_pool.Get(handle, error); + if (t) { + t->status &= ~THREADSTATUS_WAIT; + if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { + __ChangeReadyState(t, true); + } + } +} + +/// Puts a thread in the wait state for the given type/reason +void __WaitCurThread(WaitType wait_type, const char* reason) { + Thread* t = __GetCurrentThread(); + t->wait_type = wait_type; + __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); } /// Creates a new thread -Thread* __KernelCreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, +Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { Thread* t = new Thread; @@ -187,31 +245,31 @@ Thread* __KernelCreateThread(Handle& handle, const char* name, u32 entry_point, } /// Creates a new thread - wrapper for external user -Handle __KernelCreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, +Handle CreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { if (name == NULL) { - ERROR_LOG(KERNEL, "__KernelCreateThread(): NULL name"); + ERROR_LOG(KERNEL, "CreateThread(): NULL name"); return -1; } if ((u32)stack_size < 0x200) { - ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid stack_size=0x%08X", name, + ERROR_LOG(KERNEL, "CreateThread(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); - WARN_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", + WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", 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)) { - ERROR_LOG(KERNEL, "__KernelCreateThread(name=%s): invalid entry %08x", name, entry_point); + ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); return -1; } Handle handle; - Thread* t = __KernelCreateThread(handle, name, entry_point, priority, processor_id, stack_top, + Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); HLE::EatCycles(32000); @@ -220,114 +278,62 @@ Handle __KernelCreateThread(const char* name, u32 entry_point, s32 priority, s32 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. HLE::ReSchedule("thread created"); - __KernelCallThread(t); + __CallThread(t); return handle; } -/// Switches CPU context to that of the specified thread -void __KernelSwitchContext(Thread* t, const char* reason) { - Thread* cur = __GetCurrentThread(); - - // Save context for current thread - if (cur) { - __KernelSaveContext(cur->context); - - if (cur->IsRunning()) { - __KernelChangeReadyState(cur, true); - } - } - // Load context of new thread - if (t) { - __SetCurrentThread(t); - __KernelChangeReadyState(t, false); - t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - t->wait_type = WAITTYPE_NONE; - __KernelLoadContext(t->context); - } else { - __SetCurrentThread(NULL); - } -} - -/// Gets the next thread that is ready to be run by priority -Thread* __KernelNextThread() { - Handle next; - Thread* cur = __GetCurrentThread(); - - if (cur && cur->IsRunning()) { - next = g_thread_ready_queue.pop_first_better(cur->current_priority); - } else { - next = g_thread_ready_queue.pop_first(); - } - if (next < 0) { - return NULL; - } - return Kernel::g_object_pool.GetFast(next); +/// Gets the current thread +Handle GetCurrentThread() { + return __GetCurrentThread()->GetHandle(); } /// Sets up the primary application thread -Handle __KernelSetupMainThread(s32 priority, int stack_size) { +Handle SetupMainThread(s32 priority, int stack_size) { Handle handle; // Initialize new "main" thread - Thread* t = __KernelCreateThread(handle, "main", Core::g_app_core->GetPC(), priority, + Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - __KernelResetThread(t, 0); + __ResetThread(t, 0); // If running another thread already, set it to "ready" state Thread* cur = __GetCurrentThread(); if (cur && cur->IsRunning()) { - __KernelChangeReadyState(cur, true); + __ChangeReadyState(cur, true); } // Run new "main" thread __SetCurrentThread(t); t->status = THREADSTATUS_RUNNING; - __KernelLoadContext(t->context); + __LoadContext(t->context); return handle; } -/// Resumes a thread from waiting by marking it as "ready" -void __KernelResumeThreadFromWait(Handle handle) { - u32 error; - Thread* t = Kernel::g_object_pool.Get(handle, error); - if (t) { - t->status &= ~THREADSTATUS_WAIT; - if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { - __KernelChangeReadyState(t, true); - } - } -} - -/// Puts a thread in the wait state for the given type/reason -void __KernelWaitCurThread(WaitType wait_type, const char* reason) { - Thread* t = __GetCurrentThread(); - t->wait_type = wait_type; - __KernelChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); -} - /// Reschedules to the next available thread (call after current thread is suspended) -void __KernelReschedule(const char* reason) { - Thread* next = __KernelNextThread(); +void Reschedule(const char* reason) { + Thread* next = __NextThread(); if (next > 0) { - __KernelSwitchContext(next, reason); + __SwitchContext(next, reason); } } //////////////////////////////////////////////////////////////////////////////////////////////////// /// Put current thread in a wait state - on WaitSynchronization -void __KernelWaitThread_Synchronization() { +void WaitThread_Synchronization() { // TODO(bunnei): Just a placeholder function for now... FixMe - __KernelWaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); + __WaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); } //////////////////////////////////////////////////////////////////////////////////////////////////// -void __KernelThreadingInit() { +void ThreadingInit() { } -void __KernelThreadingShutdown() { +void ThreadingShutdown() { } + +} // namespace diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 72e9a416de..2c01992730 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -7,8 +7,6 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" -class Thread; - enum ThreadPriority { THREADPRIO_HIGHEST = 0, THREADPRIO_DEFAULT = 16, @@ -21,18 +19,28 @@ enum ThreadProcessorId { THREADPROCESSORID_ALL = 0xFFFFFFFC, }; +namespace Kernel { + /// Creates a new thread - wrapper for external user -Handle __KernelCreateThread(const char* name, u32 entry_point, s32 priority, - s32 processor_id, u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); +Handle CreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, + u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Sets up the primary application thread -Handle __KernelSetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); +Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Reschedules to the next available thread (call after current thread is suspended) -void __KernelReschedule(const char* reason); +void Reschedule(const char* reason); -void __KernelThreadingInit(); -void __KernelThreadingShutdown(); +/// Gets the current thread +Handle GetCurrentThread(); /// Put current thread in a wait state - on WaitSynchronization -void __KernelWaitThread_Synchronization(); +void WaitThread_Synchronization(); + +/// Initialize threading +void ThreadingInit(); + +/// Shutdown threading +void ThreadingShutdown(); + +} // namespace diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index a9141699c8..6f0f099c68 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -123,7 +123,7 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa for (u32 i = 0; i < handle_count; i++) { DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]); } - __KernelReschedule("WaitSynchronizationN"); + Kernel::Reschedule("WaitSynchronizationN"); return 0; } @@ -175,7 +175,7 @@ Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 st "threadpriority=0x%08X, processorid=0x%08X", entry_point, name.c_str(), arg, stack_top, priority, processor_id); - Handle handle = __KernelCreateThread(name.c_str(), entry_point, priority, processor_id, + Handle handle = Kernel::CreateThread(name.c_str(), entry_point, priority, processor_id, stack_top); Core::g_app_core->SetReg(1, 0xFEEDDEAF); From bed4e920fa17c6ab1e1cfde1f3ee81d0ca4aaff9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 21:00:10 -0400 Subject: [PATCH 49/72] thread: exposed ResumeThreadFromWait function for use in other kernel modules --- src/core/hle/kernel/thread.cpp | 16 ++++++++-------- src/core/hle/kernel/thread.h | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 7b4f0ea47d..af9188faa3 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -200,8 +200,15 @@ Thread* __NextThread() { return Kernel::g_object_pool.GetFast(next); } +/// Puts a thread in the wait state for the given type/reason +void __WaitCurThread(WaitType wait_type, const char* reason) { + Thread* t = __GetCurrentThread(); + t->wait_type = wait_type; + __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); +} + /// Resumes a thread from waiting by marking it as "ready" -void __ResumeThreadFromWait(Handle handle) { +void ResumeThreadFromWait(Handle handle) { u32 error; Thread* t = Kernel::g_object_pool.Get(handle, error); if (t) { @@ -212,13 +219,6 @@ void __ResumeThreadFromWait(Handle handle) { } } -/// Puts a thread in the wait state for the given type/reason -void __WaitCurThread(WaitType wait_type, const char* reason) { - Thread* t = __GetCurrentThread(); - t->wait_type = wait_type; - __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); -} - /// 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) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 2c01992730..bb48ddc79b 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -31,6 +31,9 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Reschedules to the next available thread (call after current thread is suspended) void Reschedule(const char* reason); +/// Resumes a thread from waiting by marking it as "ready" +void ResumeThreadFromWait(Handle handle); + /// Gets the current thread Handle GetCurrentThread(); From 203541da119cad61096fda20b3ff8a8cb5906fd6 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 21:02:35 -0400 Subject: [PATCH 50/72] thread: added correct lowest thread priority, added a thread priority check, and added some comments --- src/core/hle/kernel/thread.cpp | 3 +++ src/core/hle/kernel/thread.h | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index af9188faa3..294e03ca62 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -223,6 +223,9 @@ void ResumeThreadFromWait(Handle handle) { Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { + _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), + "CreateThread priority=%d, outside of allowable range!", priority) + Thread* t = new Thread; handle = Kernel::g_object_pool.Create(t); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index bb48ddc79b..0d1fe19bf2 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -8,15 +8,16 @@ #include "core/hle/kernel/kernel.h" enum ThreadPriority { - THREADPRIO_HIGHEST = 0, - THREADPRIO_DEFAULT = 16, - THREADPRIO_LOWEST = 31, + THREADPRIO_HIGHEST = 0, ///< Highest thread priority + THREADPRIO_DEFAULT = 16, ///< Default thread priority for userland apps + THREADPRIO_LOW = 31, ///< Low range of thread priority for userland apps + THREADPRIO_LOWEST = 63, ///< Thread priority max checked by svcCreateThread }; enum ThreadProcessorId { - THREADPROCESSORID_0 = 0xFFFFFFFE, - THREADPROCESSORID_1 = 0xFFFFFFFD, - THREADPROCESSORID_ALL = 0xFFFFFFFC, + THREADPROCESSORID_0 = 0xFFFFFFFE, ///< Enables core appcode + THREADPROCESSORID_1 = 0xFFFFFFFD, ///< Enables core syscore + THREADPROCESSORID_ALL = 0xFFFFFFFC, ///< Enables both cores }; namespace Kernel { From 22eb9a8981c9b4283e37f7d403c598402339ed7f Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 22:25:40 -0400 Subject: [PATCH 51/72] armemu: missed rename of "Syscall" to "SVC" --- src/core/arm/interpreter/armemu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/arm/interpreter/armemu.cpp b/src/core/arm/interpreter/armemu.cpp index aa1ff17bb2..e5dc7bd44f 100644 --- a/src/core/arm/interpreter/armemu.cpp +++ b/src/core/arm/interpreter/armemu.cpp @@ -4548,7 +4548,7 @@ ARMul_Emulate26 (ARMul_State * state) // ARMul_OSHandleSWI (state, BITS (0, 23)); // break; //} - HLE::CallSyscall(instr); + HLE::CallSVC(instr); ARMul_Abort (state, ARMul_SWIV); break; } From a721a4eb33a06b532e36e5a49578abd8338d0d28 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 22:26:40 -0400 Subject: [PATCH 52/72] arm: removed include of windows.h from armdefs.h to arminit.c (only module that uses it) --- src/core/arm/interpreter/armdefs.h | 4 ---- src/core/arm/interpreter/arminit.cpp | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/arm/interpreter/armdefs.h b/src/core/arm/interpreter/armdefs.h index 5b2abc7f74..d8eae4d3f1 100644 --- a/src/core/arm/interpreter/armdefs.h +++ b/src/core/arm/interpreter/armdefs.h @@ -24,10 +24,6 @@ #include "common/platform.h" -#if EMU_PLATFORM == PLATFORM_WINDOWS -#include -#endif - //teawater add for arm2x86 2005.02.14------------------------------------------- // koodailar remove it for mingw 2005.12.18---------------- //anthonylee modify it for portable 2007.01.30 diff --git a/src/core/arm/interpreter/arminit.cpp b/src/core/arm/interpreter/arminit.cpp index 2c771cdda5..e05667beac 100644 --- a/src/core/arm/interpreter/arminit.cpp +++ b/src/core/arm/interpreter/arminit.cpp @@ -17,8 +17,11 @@ #include "common/platform.h" + #if EMU_PLATFORM == PLATFORM_LINUX #include +#elif EMU_PLATFORM == PLATFORM_WINDOWS +#include #endif #include From 9bf7ce535a604d5523bfc14ea8057477ce1a4e0f Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 22:27:12 -0400 Subject: [PATCH 53/72] service: removed redundant include of common_types.h --- src/core/hle/service/service.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index eba730efb9..716669bedc 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -10,7 +10,6 @@ #include #include "common/common.h" -#include "common/common_types.h" #include "core/mem_map.h" #include "core/hle/kernel/kernel.h" From 1c5802c35a668cbb326cb497708bde341cd63f6d Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 22:27:46 -0400 Subject: [PATCH 54/72] kernel: fixed include, in general include "common.h" not "common_types.h" --- src/core/hle/kernel/kernel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 19edb7b57f..1803399142 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,7 +4,7 @@ #pragma once -#include "common/common_types.h" +#include "common/common.h" typedef u32 Handle; typedef s32 Result; From 08e6a9bf896b386a339fccc65066ee51c8592b9c Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 22:57:54 -0400 Subject: [PATCH 55/72] svc: added some comments --- src/core/hle/svc.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 6f0f099c68..3674a08c5a 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -10,6 +10,7 @@ #include "core/mem_map.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/thread.h" #include "core/hle/function_wrappers.h" @@ -160,6 +161,7 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* return 0; } +/// Creates a new thread Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { std::string name; @@ -182,6 +184,7 @@ Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 st return 0; } +/// Create a mutex Result CreateMutex(void* _mutex, u32 initial_locked) { Handle* mutex = (Handle*)_mutex; DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateMutex called initial_locked=%s", @@ -190,16 +193,19 @@ Result CreateMutex(void* _mutex, u32 initial_locked) { return 0; } +/// Release a mutex Result ReleaseMutex(Handle handle) { DEBUG_LOG(SVC, "(UNIMPLEMENTED) ReleaseMutex called handle=0x%08X", handle); return 0; } +/// Get current thread ID Result GetThreadId(void* thread_id, u32 thread) { DEBUG_LOG(SVC, "(UNIMPLEMENTED) GetThreadId called thread=0x%08X", thread); return 0; } +/// Query memory Result QueryMemory(void *_info, void *_out, u32 addr) { MemoryInfo* info = (MemoryInfo*) _info; PageInfo* out = (PageInfo*) _out; @@ -207,6 +213,7 @@ Result QueryMemory(void *_info, void *_out, u32 addr) { return 0; } +/// Create an event Result CreateEvent(void* _event, u32 reset_type) { Handle* event = (Handle*)_event; DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateEvent called reset_type=0x%08X", reset_type); From 978e1d4653cd12a68d6bfa05af57edb1645da0f5 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 23:03:45 -0400 Subject: [PATCH 56/72] mutex: initial commit of HLE module --- src/core/CMakeLists.txt | 1 + src/core/core.vcxproj | 2 + src/core/core.vcxproj.filters | 6 ++ src/core/hle/kernel/mutex.cpp | 122 ++++++++++++++++++++++++++++++++++ src/core/hle/kernel/mutex.h | 26 ++++++++ src/core/hle/service/apt.cpp | 10 ++- src/core/hle/svc.cpp | 9 +-- 7 files changed, 166 insertions(+), 10 deletions(-) create mode 100644 src/core/hle/kernel/mutex.cpp create mode 100644 src/core/hle/kernel/mutex.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e006502da5..6ad3087986 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -35,6 +35,7 @@ set(SRCS core.cpp hle/coprocessor.cpp hle/svc.cpp hle/kernel/kernel.cpp + hle/kernel/mutex.cpp hle/kernel/thread.cpp hle/service/apt.cpp hle/service/gsp.cpp diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 6eb58a636c..f271d336e0 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -169,6 +169,7 @@ + @@ -217,6 +218,7 @@ + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index fc4e35edb0..f664debec6 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -162,6 +162,9 @@ hle + + hle\kernel + @@ -289,6 +292,9 @@ hle + + hle\kernel + diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp new file mode 100644 index 0000000000..2b2cff4ea2 --- /dev/null +++ b/src/core/hle/kernel/mutex.cpp @@ -0,0 +1,122 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include +#include + +#include "common/common.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +class Mutex : public Object { +public: + const char* GetTypeName() { return "Mutex"; } + + static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } + Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; } + + bool initial_locked; ///< Initial lock state when mutex was created + bool locked; ///< Current locked state + Handle lock_thread; ///< Handle to thread that currently has mutex + std::vector waiting_threads; ///< Threads that are waiting for the mutex +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef std::multimap MutexMap; +static MutexMap g_mutex_held_locks; + +void __MutexAcquireLock(Mutex* mutex, Handle thread) { + g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); + mutex->lock_thread = thread; +} + +void __MutexAcquireLock(Mutex* mutex) { + Handle thread = GetCurrentThread(); + __MutexAcquireLock(mutex, thread); +} + +void __MutexEraseLock(Mutex* mutex) { + Handle handle = mutex->GetHandle(); + auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); + for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { + if ((*iter).second == handle) { + g_mutex_held_locks.erase(iter); + break; + } + } + mutex->lock_thread = -1; +} + +bool __LockMutex(Mutex* mutex) { + // Mutex alread locked? + if (mutex->locked) { + return false; + } + __MutexAcquireLock(mutex); + return true; +} + +bool __ReleaseMutexForThread(Mutex* mutex, Handle thread) { + __MutexAcquireLock(mutex, thread); + Kernel::ResumeThreadFromWait(thread); + return true; +} + +bool __ReleaseMutex(Mutex* mutex) { + __MutexEraseLock(mutex); + bool woke_threads = false; + auto iter = mutex->waiting_threads.begin(); + + // Find the next waiting thread for the mutex... + while (!woke_threads && !mutex->waiting_threads.empty()) { + woke_threads |= __ReleaseMutexForThread(mutex, *iter); + mutex->waiting_threads.erase(iter); + } + // Reset mutex lock thread handle, nothing is waiting + if (!woke_threads) { + mutex->locked = false; + mutex->lock_thread = -1; + } + return woke_threads; +} + +/** + * Releases a mutex + * @param handle Handle to mutex to release + */ +Result ReleaseMutex(Handle handle) { + Mutex* mutex = Kernel::g_object_pool.GetFast(handle); + if (!__ReleaseMutex(mutex)) { + return -1; + } + return 0; +} + +/** + * Creates a mutex + * @param handle Reference to handle for the newly created mutex + * @param initial_locked Specifies if the mutex should be locked initially + */ +Result CreateMutex(Handle& handle, bool initial_locked) { + Mutex* mutex = new Mutex; + handle = Kernel::g_object_pool.Create(mutex); + + mutex->locked = mutex->initial_locked = initial_locked; + + // Acquire mutex with current thread if initialized as locked... + if (mutex->locked) { + __MutexAcquireLock(mutex); + + // Otherwise, reset lock thread handle + } else { + mutex->lock_thread = -1; + } + return 0; +} + +} // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h new file mode 100644 index 0000000000..1f843e9792 --- /dev/null +++ b/src/core/hle/kernel/mutex.h @@ -0,0 +1,26 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +/** + * Releases a mutex + * @param handle Handle to mutex to release + */ +Result ReleaseMutex(Handle handle); + +/** + * Creates a mutex + * @param handle Reference to handle for the newly created mutex + * @param initial_locked Specifies if the mutex should be locked initially + */ +Result CreateMutex(Handle& handle, bool initial_locked); + +} // namespace diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp index 1f6a70eab9..ecec4da002 100644 --- a/src/core/hle/service/apt.cpp +++ b/src/core/hle/service/apt.cpp @@ -3,9 +3,10 @@ // Refer to the license.txt file included. -#include "common/log.h" +#include "common/common.h" #include "core/hle/hle.h" +#include "core/hle/kernel/mutex.h" #include "core/hle/service/apt.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -19,11 +20,8 @@ void Initialize(Service::Interface* self) { void GetLockHandle(Service::Interface* self) { u32* cmd_buff = Service::GetCommandBuffer(); - u32 flags = cmd_buff[1]; - - // TODO: This should be an actual mutex handle. Games will check that this is not non-zero - // (NULL), and fail if such. A faked non-zero value will at least enable further booting. - cmd_buff[5] = 0x12345678; + u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field + cmd_buff[1] = Kernel::CreateMutex(cmd_buff[5], false); } const Interface::FunctionInfo FunctionTable[] = { diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 3674a08c5a..73ab073f55 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -187,15 +187,16 @@ Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 st /// Create a mutex Result CreateMutex(void* _mutex, u32 initial_locked) { Handle* mutex = (Handle*)_mutex; - DEBUG_LOG(SVC, "(UNIMPLEMENTED) CreateMutex called initial_locked=%s", - initial_locked ? "true" : "false"); - Core::g_app_core->SetReg(1, 0xF00D0BAD); + DEBUG_LOG(SVC, "CreateMutex called initial_locked=%s", initial_locked ? "true" : "false"); + Kernel::CreateMutex(*mutex, (bool)initial_locked); + Core::g_app_core->SetReg(1, *mutex); return 0; } /// Release a mutex Result ReleaseMutex(Handle handle) { - DEBUG_LOG(SVC, "(UNIMPLEMENTED) ReleaseMutex called handle=0x%08X", handle); + DEBUG_LOG(SVC, "ReleaseMutex called handle=0x%08X", handle); + Kernel::ReleaseMutex(handle); return 0; } From eb537c560a33db9955413a96afd3b98203a729fe Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 23:23:58 -0400 Subject: [PATCH 57/72] mutex: refactored the interface to code to return a Mutex* handle --- src/core/hle/kernel/mutex.cpp | 14 ++++++++++++-- src/core/hle/kernel/mutex.h | 2 +- src/core/hle/service/apt.cpp | 3 ++- src/core/hle/svc.cpp | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 2b2cff4ea2..7cf3439e98 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -102,7 +102,7 @@ Result ReleaseMutex(Handle handle) { * @param handle Reference to handle for the newly created mutex * @param initial_locked Specifies if the mutex should be locked initially */ -Result CreateMutex(Handle& handle, bool initial_locked) { +Mutex* CreateMutex(Handle& handle, bool initial_locked) { Mutex* mutex = new Mutex; handle = Kernel::g_object_pool.Create(mutex); @@ -116,7 +116,17 @@ Result CreateMutex(Handle& handle, bool initial_locked) { } else { mutex->lock_thread = -1; } - return 0; + return mutex; +} + +/** + * Creates a mutex + * @param initial_locked Specifies if the mutex should be locked initially + */ +Handle CreateMutex(bool initial_locked) { + Handle handle; + Mutex* mutex = CreateMutex(handle, initial_locked); + return handle; } } // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 1f843e9792..871e2e5626 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -21,6 +21,6 @@ Result ReleaseMutex(Handle handle); * @param handle Reference to handle for the newly created mutex * @param initial_locked Specifies if the mutex should be locked initially */ -Result CreateMutex(Handle& handle, bool initial_locked); +Handle CreateMutex(bool initial_locked); } // namespace diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp index ecec4da002..b01f35ac52 100644 --- a/src/core/hle/service/apt.cpp +++ b/src/core/hle/service/apt.cpp @@ -21,7 +21,8 @@ void Initialize(Service::Interface* self) { void GetLockHandle(Service::Interface* self) { u32* cmd_buff = Service::GetCommandBuffer(); u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field - cmd_buff[1] = Kernel::CreateMutex(cmd_buff[5], false); + cmd_buff[1] = 0; // No error + cmd_buff[5] = Kernel::CreateMutex(false); } const Interface::FunctionInfo FunctionTable[] = { diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 73ab073f55..ffacdfb86d 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -188,7 +188,7 @@ Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 st Result CreateMutex(void* _mutex, u32 initial_locked) { Handle* mutex = (Handle*)_mutex; DEBUG_LOG(SVC, "CreateMutex called initial_locked=%s", initial_locked ? "true" : "false"); - Kernel::CreateMutex(*mutex, (bool)initial_locked); + *mutex = Kernel::CreateMutex((initial_locked != 0)); Core::g_app_core->SetReg(1, *mutex); return 0; } From 706584f007f48cd319fe7511969f914843951088 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 20 May 2014 23:36:35 -0400 Subject: [PATCH 58/72] svc: enabled use of newly created kernel thread handle --- src/core/hle/svc.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index ffacdfb86d..6f6f5b2f59 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -162,8 +162,7 @@ Result GetResourceLimitCurrentValues(void* _values, Handle resource_limit, void* } /// Creates a new thread -Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 stack_top, - u32 processor_id) { +Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { std::string name; if (Symbols::HasSymbol(entry_point)) { TSymbol symbol = Symbols::GetSymbol(entry_point); @@ -177,9 +176,10 @@ Result CreateThread(void* thread, u32 priority, u32 entry_point, u32 arg, u32 st "threadpriority=0x%08X, processorid=0x%08X", entry_point, name.c_str(), arg, stack_top, priority, processor_id); - Handle handle = Kernel::CreateThread(name.c_str(), entry_point, priority, processor_id, + Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, processor_id, stack_top); - Core::g_app_core->SetReg(1, 0xFEEDDEAF); + + Core::g_app_core->SetReg(1, thread); return 0; } @@ -231,7 +231,7 @@ const HLE::FunctionDef SVC_Table[] = { {0x05, NULL, "SetProcessAffinityMask"}, {0x06, NULL, "GetProcessIdealProcessor"}, {0x07, NULL, "SetProcessIdealProcessor"}, - {0x08, WrapI_VUUUUU, "CreateThread"}, + {0x08, WrapI_UUUUU, "CreateThread"}, {0x09, NULL, "ExitThread"}, {0x0A, NULL, "SleepThread"}, {0x0B, NULL, "GetThreadPriority"}, From 06e3c3d55a6e8e58295bd9e7bebecdc777145b20 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 21 May 2014 21:41:40 -0400 Subject: [PATCH 59/72] svc: added Kernel::Reschedule to svc WaitSynchronization1, updated log messages to include newly created handles --- src/core/hle/svc.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 6f6f5b2f59..14d512b993 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -109,6 +109,7 @@ Result WaitSynchronization1(Handle handle, s64 nano_seconds) { // ImplementMe DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d", handle, nano_seconds); + Kernel::Reschedule("WaitSynchronization1"); return 0; } @@ -172,14 +173,15 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p sprintf(buff, "%s", "unknown-%08X", entry_point); name = buff; } - DEBUG_LOG(SVC, "CreateThread called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " - "threadpriority=0x%08X, processorid=0x%08X", entry_point, name.c_str(), arg, stack_top, - priority, processor_id); Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, processor_id, stack_top); Core::g_app_core->SetReg(1, thread); + + DEBUG_LOG(SVC, "CreateThread 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); return 0; } @@ -187,9 +189,10 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p /// Create a mutex Result CreateMutex(void* _mutex, u32 initial_locked) { Handle* mutex = (Handle*)_mutex; - DEBUG_LOG(SVC, "CreateMutex called initial_locked=%s", initial_locked ? "true" : "false"); *mutex = Kernel::CreateMutex((initial_locked != 0)); Core::g_app_core->SetReg(1, *mutex); + DEBUG_LOG(SVC, "CreateMutex called initial_locked=%s : created handle 0x%08X", + initial_locked ? "true" : "false", *mutex); return 0; } From 6a78be5930fd5eabbea740a5cf17efa5a7f1bc98 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 21 May 2014 21:42:18 -0400 Subject: [PATCH 60/72] thread: fixed bug where result of __NextThread was not being properly checked when NULL --- src/core/hle/kernel/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 294e03ca62..ef705e327d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -194,7 +194,7 @@ Thread* __NextThread() { } else { next = g_thread_ready_queue.pop_first(); } - if (next < 0) { + if (next == 0) { return NULL; } return Kernel::g_object_pool.GetFast(next); From 481d936d34c595b994f48b00819bb2c4bcfa7e57 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 18:47:42 -0400 Subject: [PATCH 61/72] arm_interpreter: fixed load context to currently resume a thread --- src/core/arm/interpreter/arm_interpreter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/arm/interpreter/arm_interpreter.cpp b/src/core/arm/interpreter/arm_interpreter.cpp index a9ec948207..17f787b862 100644 --- a/src/core/arm/interpreter/arm_interpreter.cpp +++ b/src/core/arm/interpreter/arm_interpreter.cpp @@ -115,7 +115,7 @@ void ARM_Interpreter::SaveContext(ThreadContext& ctx) { ctx.lr = state->Reg[14]; ctx.pc = state->pc; ctx.cpsr = state->Cpsr; - + ctx.fpscr = state->VFP[1]; ctx.fpexc = state->VFP[2]; } @@ -136,4 +136,7 @@ void ARM_Interpreter::LoadContext(const ThreadContext& ctx) { state->VFP[1] = ctx.fpscr; state->VFP[2] = ctx.fpexc; + + state->Reg[15] = ctx.pc; + state->NextInstr = RESUME; } From 9fddba68435f6e276bfb40c793f1ca0f8b984ec9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 18:48:14 -0400 Subject: [PATCH 62/72] APT_U: added a debug log on calling GetLockHandle --- src/core/hle/service/apt.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/hle/service/apt.cpp b/src/core/hle/service/apt.cpp index b01f35ac52..32759a0874 100644 --- a/src/core/hle/service/apt.cpp +++ b/src/core/hle/service/apt.cpp @@ -23,6 +23,7 @@ void GetLockHandle(Service::Interface* self) { u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field cmd_buff[1] = 0; // No error cmd_buff[5] = Kernel::CreateMutex(false); + DEBUG_LOG(KERNEL, "APT_U::GetLockHandle called : created handle 0x%08X", cmd_buff[5]); } const Interface::FunctionInfo FunctionTable[] = { From 14bd37c5dc67c7777d4bea8d996bf2dfd8c7bdcc Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 18:50:36 -0400 Subject: [PATCH 63/72] thread: moved ThreadStatus/WaitType to header, added support for arg on CreateThread, added correct CPSR reset --- src/core/hle/kernel/thread.cpp | 49 +++++++++++----------------------- src/core/hle/kernel/thread.h | 26 +++++++++++++++++- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index ef705e327d..934ca87c4d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -21,27 +21,6 @@ namespace Kernel { -enum ThreadStatus { - THREADSTATUS_RUNNING = 1, - THREADSTATUS_READY = 2, - THREADSTATUS_WAIT = 4, - THREADSTATUS_SUSPEND = 8, - THREADSTATUS_DORMANT = 16, - THREADSTATUS_DEAD = 32, - THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND -}; - -enum WaitType { - WAITTYPE_NONE, - WAITTYPE_SLEEP, - WAITTYPE_SEMA, - WAITTYPE_EVENTFLAG, - WAITTYPE_THREADEND, - WAITTYPE_VBLANK, - WAITTYPE_MUTEX, - WAITTYPE_SYNCH, -}; - class Thread : public Kernel::Object { public: @@ -101,16 +80,18 @@ void __SaveContext(ThreadContext& ctx) { } /// Loads a CPU context -void __LoadContext(const ThreadContext& ctx) { +void __LoadContext(ThreadContext& ctx) { Core::g_app_core->LoadContext(ctx); } /// Resets a thread -void __ResetThread(Thread* t, s32 lowest_priority) { +void __ResetThread(Thread* t, u32 arg, s32 lowest_priority) { memset(&t->context, 0, sizeof(ThreadContext)); + t->context.cpu_registers[0] = arg; t->context.pc = t->entry_point; t->context.sp = t->stack_top; + t->context.cpsr = 0x1F; // Usermode if (t->current_priority < lowest_priority) { t->current_priority = t->initial_priority; @@ -201,7 +182,7 @@ Thread* __NextThread() { } /// Puts a thread in the wait state for the given type/reason -void __WaitCurThread(WaitType wait_type, const char* reason) { +void WaitCurThread(WaitType wait_type, const char* reason) { Thread* t = __GetCurrentThread(); t->wait_type = wait_type; __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); @@ -248,7 +229,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio } /// Creates a new thread - wrapper for external user -Handle CreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, +Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, u32 stack_top, int stack_size) { if (name == NULL) { ERROR_LOG(KERNEL, "CreateThread(): NULL name"); @@ -275,6 +256,8 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, s32 process Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); + __ResetThread(t, arg, 0); + HLE::EatCycles(32000); // This won't schedule to the new thread, but it may to one woken from eating cycles. @@ -299,7 +282,7 @@ Handle SetupMainThread(s32 priority, int stack_size) { Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - __ResetThread(t, 0); + __ResetThread(t, 0, 0); // If running another thread already, set it to "ready" state Thread* cur = __GetCurrentThread(); @@ -317,22 +300,20 @@ Handle SetupMainThread(s32 priority, int stack_size) { /// Reschedules to the next available thread (call after current thread is suspended) void Reschedule(const char* reason) { + Thread* prev = __GetCurrentThread(); Thread* next = __NextThread(); if (next > 0) { __SwitchContext(next, reason); + + // Hack - automatically change previous thread (which would have been in "wait" state) to + // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to + // actually wait for whatever event it is supposed to be waiting on. + __ChangeReadyState(prev, true); } } //////////////////////////////////////////////////////////////////////////////////////////////////// -/// Put current thread in a wait state - on WaitSynchronization -void WaitThread_Synchronization() { - // TODO(bunnei): Just a placeholder function for now... FixMe - __WaitCurThread(WAITTYPE_SYNCH, "waitSynchronization called"); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - void ThreadingInit() { } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0d1fe19bf2..82bf16082e 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -20,10 +20,31 @@ enum ThreadProcessorId { THREADPROCESSORID_ALL = 0xFFFFFFFC, ///< Enables both cores }; +enum ThreadStatus { + THREADSTATUS_RUNNING = 1, + THREADSTATUS_READY = 2, + THREADSTATUS_WAIT = 4, + THREADSTATUS_SUSPEND = 8, + THREADSTATUS_DORMANT = 16, + THREADSTATUS_DEAD = 32, + THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND +}; + +enum WaitType { + WAITTYPE_NONE, + WAITTYPE_SLEEP, + WAITTYPE_SEMA, + WAITTYPE_EVENTFLAG, + WAITTYPE_THREADEND, + WAITTYPE_VBLANK, + WAITTYPE_MUTEX, + WAITTYPE_SYNCH, +}; + namespace Kernel { /// Creates a new thread - wrapper for external user -Handle CreateThread(const char* name, u32 entry_point, s32 priority, s32 processor_id, +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); /// Sets up the primary application thread @@ -32,6 +53,9 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Reschedules to the next available thread (call after current thread is suspended) void Reschedule(const char* reason); +/// Puts a thread in the wait state for the given type/reason +void WaitCurThread(WaitType wait_type, const char* reason); + /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle); From ac21e8f2db8fbedeacbcef2989e8dbad524b9d44 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 18:51:55 -0400 Subject: [PATCH 64/72] core: moved armcopro.cpp to correct filter folder (arm/interpreter) --- src/core/core.vcxproj.filters | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index f664debec6..b6c1d5b935 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -147,9 +147,6 @@ arm\interpreter\mmu - - arm - arm\interpreter\mmu @@ -165,6 +162,9 @@ hle\kernel + + arm\interpreter + From d26f3d4c1ff27f740fe7185e1bca7dcfc5851919 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 19:06:12 -0400 Subject: [PATCH 65/72] kernel: refactored function naming to remove "__" prefix --- src/core/hle/kernel/kernel.cpp | 15 ++++--- src/core/hle/kernel/kernel.h | 9 +++- src/core/hle/kernel/mutex.cpp | 28 ++++++------ src/core/hle/kernel/thread.cpp | 78 +++++++++++++++++----------------- src/core/hle/kernel/thread.h | 4 +- src/core/loader.cpp | 6 +-- 6 files changed, 75 insertions(+), 65 deletions(-) diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 45e36173c0..de80de8937 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -139,15 +139,20 @@ void Shutdown() { Kernel::ThreadingShutdown(); } -} // namespace - -bool __KernelLoadExec(u32 entry_point) { - Kernel::Init(); +/** + * Loads executable stored at specified address + * @entry_point Entry point in memory of loaded executable + * @return True on success, otherwise false + */ +bool LoadExec(u32 entry_point) { + Init(); Core::g_app_core->SetPC(entry_point); // 0x30 is the typical main thread priority I've seen used so far - Handle thread_id = Kernel::SetupMainThread(0x30); + Handle thread = Kernel::SetupMainThread(0x30); return true; } + +} // namespace diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 1803399142..7cd79c2c41 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -144,6 +144,11 @@ private: extern ObjectPool g_object_pool; -} // namespace +/** + * Loads executable stored at specified address + * @entry_point Entry point in memory of loaded executable + * @return True on success, otherwise false + */ +bool LoadExec(u32 entry_point); -bool __KernelLoadExec(u32 entry_point); +} // namespace diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 7cf3439e98..019efbc785 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -30,17 +30,17 @@ public: typedef std::multimap MutexMap; static MutexMap g_mutex_held_locks; -void __MutexAcquireLock(Mutex* mutex, Handle thread) { +void MutexAcquireLock(Mutex* mutex, Handle thread) { g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); mutex->lock_thread = thread; } -void __MutexAcquireLock(Mutex* mutex) { - Handle thread = GetCurrentThread(); - __MutexAcquireLock(mutex, thread); +void MutexAcquireLock(Mutex* mutex) { + Handle thread = GetCurrentThreadHandle(); + MutexAcquireLock(mutex, thread); } -void __MutexEraseLock(Mutex* mutex) { +void MutexEraseLock(Mutex* mutex) { Handle handle = mutex->GetHandle(); auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { @@ -52,29 +52,29 @@ void __MutexEraseLock(Mutex* mutex) { mutex->lock_thread = -1; } -bool __LockMutex(Mutex* mutex) { +bool LockMutex(Mutex* mutex) { // Mutex alread locked? if (mutex->locked) { return false; } - __MutexAcquireLock(mutex); + MutexAcquireLock(mutex); return true; } -bool __ReleaseMutexForThread(Mutex* mutex, Handle thread) { - __MutexAcquireLock(mutex, thread); +bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { + MutexAcquireLock(mutex, thread); Kernel::ResumeThreadFromWait(thread); return true; } -bool __ReleaseMutex(Mutex* mutex) { - __MutexEraseLock(mutex); +bool ReleaseMutex(Mutex* mutex) { + MutexEraseLock(mutex); bool woke_threads = false; auto iter = mutex->waiting_threads.begin(); // Find the next waiting thread for the mutex... while (!woke_threads && !mutex->waiting_threads.empty()) { - woke_threads |= __ReleaseMutexForThread(mutex, *iter); + woke_threads |= ReleaseMutexForThread(mutex, *iter); mutex->waiting_threads.erase(iter); } // Reset mutex lock thread handle, nothing is waiting @@ -91,7 +91,7 @@ bool __ReleaseMutex(Mutex* mutex) { */ Result ReleaseMutex(Handle handle) { Mutex* mutex = Kernel::g_object_pool.GetFast(handle); - if (!__ReleaseMutex(mutex)) { + if (!ReleaseMutex(mutex)) { return -1; } return 0; @@ -110,7 +110,7 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked) { // Acquire mutex with current thread if initialized as locked... if (mutex->locked) { - __MutexAcquireLock(mutex); + MutexAcquireLock(mutex); // Otherwise, reset lock thread handle } else { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 934ca87c4d..5f1d5c4002 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -64,28 +64,33 @@ Thread* g_current_thread; /// Gets the current thread -inline Thread* __GetCurrentThread() { +inline Thread* GetCurrentThread() { return g_current_thread; } +/// Gets the current thread handle +Handle GetCurrentThreadHandle() { + return GetCurrentThread()->GetHandle(); +} + /// Sets the current thread -inline void __SetCurrentThread(Thread* t) { +inline void SetCurrentThread(Thread* t) { g_current_thread = t; g_current_thread_handle = t->GetHandle(); } /// Saves the current CPU context -void __SaveContext(ThreadContext& ctx) { +void SaveContext(ThreadContext& ctx) { Core::g_app_core->SaveContext(ctx); } /// Loads a CPU context -void __LoadContext(ThreadContext& ctx) { +void LoadContext(ThreadContext& ctx) { Core::g_app_core->LoadContext(ctx); } /// Resets a thread -void __ResetThread(Thread* t, u32 arg, s32 lowest_priority) { +void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { memset(&t->context, 0, sizeof(ThreadContext)); t->context.cpu_registers[0] = arg; @@ -101,7 +106,7 @@ void __ResetThread(Thread* t, u32 arg, s32 lowest_priority) { } /// Change a thread to "ready" state -void __ChangeReadyState(Thread* t, bool ready) { +void ChangeReadyState(Thread* t, bool ready) { Handle handle = t->GetHandle(); if (t->IsReady()) { if (!ready) { @@ -118,11 +123,11 @@ void __ChangeReadyState(Thread* t, bool ready) { } /// Changes a threads state -void __ChangeThreadState(Thread* t, ThreadStatus new_status) { +void ChangeThreadState(Thread* t, ThreadStatus new_status) { if (!t || t->status == new_status) { return; } - __ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); + ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; if (new_status == THREADSTATUS_WAIT) { @@ -133,42 +138,42 @@ void __ChangeThreadState(Thread* t, ThreadStatus new_status) { } /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) -void __CallThread(Thread* t) { +void CallThread(Thread* t) { // Stop waiting if (t->wait_type != WAITTYPE_NONE) { t->wait_type = WAITTYPE_NONE; } - __ChangeThreadState(t, THREADSTATUS_READY); + ChangeThreadState(t, THREADSTATUS_READY); } /// Switches CPU context to that of the specified thread -void __SwitchContext(Thread* t, const char* reason) { - Thread* cur = __GetCurrentThread(); +void SwitchContext(Thread* t, const char* reason) { + Thread* cur = GetCurrentThread(); // Save context for current thread if (cur) { - __SaveContext(cur->context); + SaveContext(cur->context); if (cur->IsRunning()) { - __ChangeReadyState(cur, true); + ChangeReadyState(cur, true); } } // Load context of new thread if (t) { - __SetCurrentThread(t); - __ChangeReadyState(t, false); + SetCurrentThread(t); + ChangeReadyState(t, false); t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; t->wait_type = WAITTYPE_NONE; - __LoadContext(t->context); + LoadContext(t->context); } else { - __SetCurrentThread(NULL); + SetCurrentThread(NULL); } } /// Gets the next thread that is ready to be run by priority -Thread* __NextThread() { +Thread* NextThread() { Handle next; - Thread* cur = __GetCurrentThread(); + Thread* cur = GetCurrentThread(); if (cur && cur->IsRunning()) { next = g_thread_ready_queue.pop_first_better(cur->current_priority); @@ -183,9 +188,9 @@ Thread* __NextThread() { /// Puts a thread in the wait state for the given type/reason void WaitCurThread(WaitType wait_type, const char* reason) { - Thread* t = __GetCurrentThread(); + Thread* t = GetCurrentThread(); t->wait_type = wait_type; - __ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); + ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); } /// Resumes a thread from waiting by marking it as "ready" @@ -195,7 +200,7 @@ void ResumeThreadFromWait(Handle handle) { if (t) { t->status &= ~THREADSTATUS_WAIT; if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { - __ChangeReadyState(t, true); + ChangeReadyState(t, true); } } } @@ -256,7 +261,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); - __ResetThread(t, arg, 0); + ResetThread(t, arg, 0); HLE::EatCycles(32000); @@ -264,16 +269,11 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. HLE::ReSchedule("thread created"); - __CallThread(t); + CallThread(t); return handle; } -/// Gets the current thread -Handle GetCurrentThread() { - return __GetCurrentThread()->GetHandle(); -} - /// Sets up the primary application thread Handle SetupMainThread(s32 priority, int stack_size) { Handle handle; @@ -282,33 +282,33 @@ Handle SetupMainThread(s32 priority, int stack_size) { Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - __ResetThread(t, 0, 0); + ResetThread(t, 0, 0); // If running another thread already, set it to "ready" state - Thread* cur = __GetCurrentThread(); + Thread* cur = GetCurrentThread(); if (cur && cur->IsRunning()) { - __ChangeReadyState(cur, true); + ChangeReadyState(cur, true); } // Run new "main" thread - __SetCurrentThread(t); + SetCurrentThread(t); t->status = THREADSTATUS_RUNNING; - __LoadContext(t->context); + LoadContext(t->context); return handle; } /// Reschedules to the next available thread (call after current thread is suspended) void Reschedule(const char* reason) { - Thread* prev = __GetCurrentThread(); - Thread* next = __NextThread(); + Thread* prev = GetCurrentThread(); + Thread* next = NextThread(); if (next > 0) { - __SwitchContext(next, reason); + SwitchContext(next, reason); // Hack - automatically change previous thread (which would have been in "wait" state) to // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to // actually wait for whatever event it is supposed to be waiting on. - __ChangeReadyState(prev, true); + ChangeReadyState(prev, true); } } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 82bf16082e..a9e9eb95fb 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -59,8 +59,8 @@ void WaitCurThread(WaitType wait_type, const char* reason); /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle); -/// Gets the current thread -Handle GetCurrentThread(); +/// Gets the current thread handle +Handle GetCurrentThreadHandle(); /// Put current thread in a wait state - on WaitSynchronization void WaitThread_Synchronization(); diff --git a/src/core/loader.cpp b/src/core/loader.cpp index 444b75feba..ff1c873bb6 100644 --- a/src/core/loader.cpp +++ b/src/core/loader.cpp @@ -56,7 +56,7 @@ bool Load_ELF(std::string &filename) { elf_reader = new ElfReader(buffer); elf_reader->LoadInto(0x00100000); - __KernelLoadExec(elf_reader->GetEntryPoint()); + Kernel::LoadExec(elf_reader->GetEntryPoint()); delete[] buffer; delete elf_reader; @@ -102,7 +102,7 @@ bool Load_DAT(std::string &filename) { *d++ = (*s++); } - __KernelLoadExec(entry_point); + Kernel::LoadExec(entry_point); delete[] buffer; @@ -144,7 +144,7 @@ bool Load_BIN(std::string &filename) { *d++ = (*s++); } - __KernelLoadExec(entry_point); + Kernel::LoadExec(entry_point); delete[] buffer; } From 7c0b0060764e75738bc9d4417d0bfd510e54ae4e Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 19:32:45 -0400 Subject: [PATCH 66/72] thread: removed unused SwitchContext/Reschedule reason field, added missing arg parameter to SVC CreateThread --- src/core/hle/kernel/thread.cpp | 6 +++--- src/core/hle/kernel/thread.h | 2 +- src/core/hle/svc.cpp | 13 +++++-------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 5f1d5c4002..189f7d5f50 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -147,7 +147,7 @@ void CallThread(Thread* t) { } /// Switches CPU context to that of the specified thread -void SwitchContext(Thread* t, const char* reason) { +void SwitchContext(Thread* t) { Thread* cur = GetCurrentThread(); // Save context for current thread @@ -299,11 +299,11 @@ Handle SetupMainThread(s32 priority, int stack_size) { } /// Reschedules to the next available thread (call after current thread is suspended) -void Reschedule(const char* reason) { +void Reschedule() { Thread* prev = GetCurrentThread(); Thread* next = NextThread(); if (next > 0) { - SwitchContext(next, reason); + SwitchContext(next); // Hack - automatically change previous thread (which would have been in "wait" state) to // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index a9e9eb95fb..d54f47aaf7 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -51,7 +51,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Reschedules to the next available thread (call after current thread is suspended) -void Reschedule(const char* reason); +void Reschedule(); /// Puts a thread in the wait state for the given type/reason void WaitCurThread(WaitType wait_type, const char* reason); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 14d512b993..b1854a36e2 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -106,10 +106,9 @@ Result CloseHandle(Handle handle) { /// Wait for a handle to synchronize, timeout after the specified nanoseconds Result WaitSynchronization1(Handle handle, s64 nano_seconds) { - // ImplementMe DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d", handle, nano_seconds); - Kernel::Reschedule("WaitSynchronization1"); + Kernel::WaitCurThread(WAITTYPE_SYNCH, "WaitSynchronization1"); // TODO(bunnei): Is this correct? return 0; } @@ -117,16 +116,14 @@ Result WaitSynchronization1(Handle handle, s64 nano_seconds) { Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wait_all, s64 nano_seconds) { s32* out = (s32*)_out; Handle* handles = (Handle*)_handles; - // ImplementMe - + DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronizationN called handle_count=%d, wait_all=%s, nanoseconds=%d %s", handle_count, (wait_all ? "true" : "false"), nano_seconds); - + for (u32 i = 0; i < handle_count; i++) { DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]); } - Kernel::Reschedule("WaitSynchronizationN"); - + Kernel::WaitCurThread(WAITTYPE_SYNCH, "WaitSynchronizationN"); // TODO(bunnei): Is this correct? return 0; } @@ -174,7 +171,7 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p name = buff; } - Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, processor_id, + Handle thread = Kernel::CreateThread(name.c_str(), entry_point, priority, arg, processor_id, stack_top); Core::g_app_core->SetReg(1, thread); From b99ac2c3d67e2bcaa2c6eac220f1e93f4576c4fe Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 19:36:56 -0400 Subject: [PATCH 67/72] thread: renamed "WaitCurThread" to "WaitCurrentThread", removed unused "reason" argument --- src/core/hle/kernel/thread.cpp | 4 ++-- src/core/hle/kernel/thread.h | 4 ++-- src/core/hle/svc.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 189f7d5f50..bf4c8353ce 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -186,8 +186,8 @@ Thread* NextThread() { return Kernel::g_object_pool.GetFast(next); } -/// Puts a thread in the wait state for the given type/reason -void WaitCurThread(WaitType wait_type, const char* reason) { +/// Puts the current thread in the wait state for the given type +void WaitCurrentThread(WaitType wait_type) { Thread* t = GetCurrentThread(); t->wait_type = wait_type; ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index d54f47aaf7..9628f165d6 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -53,8 +53,8 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); /// Reschedules to the next available thread (call after current thread is suspended) void Reschedule(); -/// Puts a thread in the wait state for the given type/reason -void WaitCurThread(WaitType wait_type, const char* reason); +/// Puts the current thread in the wait state for the given type +void WaitCurrentThread(WaitType wait_type); /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index b1854a36e2..8018a43a27 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -108,7 +108,7 @@ Result CloseHandle(Handle handle) { Result WaitSynchronization1(Handle handle, s64 nano_seconds) { DEBUG_LOG(SVC, "(UNIMPLEMENTED) WaitSynchronization1 called handle=0x%08X, nanoseconds=%d", handle, nano_seconds); - Kernel::WaitCurThread(WAITTYPE_SYNCH, "WaitSynchronization1"); // TODO(bunnei): Is this correct? + Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? return 0; } @@ -123,7 +123,7 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa for (u32 i = 0; i < handle_count; i++) { DEBUG_LOG(SVC, "\thandle[%d]=0x%08X", i, handles[i]); } - Kernel::WaitCurThread(WAITTYPE_SYNCH, "WaitSynchronizationN"); // TODO(bunnei): Is this correct? + Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? return 0; } From 7bb66421a08ee4c061d7336efc20c69f70e97cd9 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 20:01:04 -0400 Subject: [PATCH 68/72] lcd: added thread wait after (faked) vblank interrupt (would have) occurred --- src/core/hw/lcd.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp index 6468053f27..b57563a73a 100644 --- a/src/core/hw/lcd.cpp +++ b/src/core/hw/lcd.cpp @@ -11,6 +11,8 @@ #include "video_core/video_core.h" +#include "core/hle/kernel/thread.h" + namespace LCD { Registers g_regs; @@ -130,9 +132,11 @@ template void Write(u32 addr, const u8 data); void Update() { u64 current_ticks = Core::g_app_core->GetTicks(); + // Fake a vertical blank if ((current_ticks - g_last_ticks) >= kFrameTicks) { g_last_ticks = current_ticks; VideoCore::g_renderer->SwapBuffers(); + Kernel::WaitCurrentThread(WAITTYPE_VBLANK); } } From f8a98ab0228f25431d2df87f88310b41bb381b8e Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 22:49:29 -0400 Subject: [PATCH 69/72] svc: added a check to ensure that a service was implemented before attempting to connect to its port --- src/core/hle/svc.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 8018a43a27..90c05cb74d 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -84,7 +84,11 @@ Result MapMemoryBlock(Handle memblock, u32 addr, u32 mypermissions, u32 otherper /// Connect to an OS service given the port name, returns the handle to the port to out Result ConnectToPort(void* out, const char* port_name) { Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); - Core::g_app_core->SetReg(1, service->GetHandle()); + if (service) { + Core::g_app_core->SetReg(1, service->GetHandle()); + } else { + PanicYesNo("ConnectToPort called port_name=%s, but it is not implemented!", port_name); + } DEBUG_LOG(SVC, "ConnectToPort called port_name=%s", port_name); return 0; } From 51f636b3d44e0c963d73cbc4e1a555633980b3fb Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 22:54:07 -0400 Subject: [PATCH 70/72] core: added Kernel::Reschedule() call to check for thread changes, shortened delay time to 100 instructions --- src/core/core.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/core.cpp b/src/core/core.cpp index 61c237b2c3..f88bcd704e 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -12,6 +12,8 @@ #include "core/arm/disassembler/arm_disasm.h" #include "core/arm/interpreter/arm_interpreter.h" +#include "core/hle/kernel/thread.h" + namespace Core { ARM_Disasm* g_disasm = NULL; ///< ARM disassembler @@ -21,14 +23,17 @@ ARM_Interface* g_sys_core = NULL; ///< ARM11 system (OS) core /// Run the core CPU loop void RunLoop() { for (;;){ - g_app_core->Run(10000); + g_app_core->Run(100); HW::Update(); + Kernel::Reschedule(); } } /// Step the CPU one instruction void SingleStep() { g_app_core->Step(); + HW::Update(); + Kernel::Reschedule(); } /// Halt the core From 420971a849b5811e4a6ae866292e70f40a716e5c Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 23:39:25 -0400 Subject: [PATCH 71/72] core: fixed headers in CMakeLists --- src/core/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b63404f137..4086b415bf 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -78,7 +78,10 @@ set(HEADERS core.h hle/config_mem.h hle/coprocessor.h hle/hle.h - hle/syscall.h + hle/svc.h + hle/kernel/kernel.h + hle/kernel/mutex.h + hle/kernel/thread.h hle/function_wrappers.h hle/service/apt.h hle/service/gsp.h From 0aa582bf89c3e3e479540b706511590636870912 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 22 May 2014 23:48:37 -0400 Subject: [PATCH 72/72] service: fixed typo that MSVC did not catch as an error --- src/core/hle/service/service.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 716669bedc..fab51753f9 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -71,7 +71,7 @@ public: /// Frees a handle from the service template void DeleteHandle(const Handle handle) { - g_object_pool.Destroy(handle); + Kernel::g_object_pool.Destroy(handle); m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); }