From 6b264518a50ce21cb1be55ff3eac4e1c85582cfe Mon Sep 17 00:00:00 2001 From: bunnei Date: Fri, 9 May 2014 22:11:18 -0400 Subject: [PATCH] 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 eb4fef381..3fa5f51f1 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 b56661e48..59fc6f4fc 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 5c947ec23..ff988c116 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 000000000..fc494fe30 --- /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 000000000..2381ca7f7 --- /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 000000000..0ed35de83 --- /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 000000000..c3cdca31f --- /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