// Copyright (c) 2012- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #pragma once #include #include #include #include #include "Common/CommonTypes.h" #include "Core/HLE/sceKernel.h" #include "Core/HLE/PSPThreadContext.h" #include "Core/HLE/KernelThreadDebugInterface.h" // There's a good description of the thread scheduling rules in: // http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/ThreadManForUser.java class PSPThread; class DebugInterface; class BlockAllocator; int sceKernelChangeThreadPriority(SceUID threadID, int priority); SceUID __KernelCreateThreadInternal(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr); int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr, bool allowKernel); int sceKernelCreateThread(const char *threadName, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr); int sceKernelDelayThread(u32 usec); int sceKernelDelayThreadCB(u32 usec); int sceKernelDelaySysClockThread(u32 sysclockAddr); int sceKernelDelaySysClockThreadCB(u32 sysclockAddr); void __KernelStopThread(SceUID threadID, int exitStatus, const char *reason); u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason); int sceKernelDeleteThread(int threadHandle); int sceKernelExitDeleteThread(int exitStatus); int sceKernelExitThread(int exitStatus); void _sceKernelExitThread(int exitStatus); SceUID sceKernelGetThreadId(); int sceKernelGetThreadCurrentPriority(); // Warning: will alter v0 in current MIPS state. int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bool forceArgs = false); int __KernelStartThreadValidate(SceUID threadToStartID, int argSize, u32 argBlockPtr, bool forceArgs = false); int __KernelGetThreadExitStatus(SceUID threadID); int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr); u32 sceKernelSuspendDispatchThread(); u32 sceKernelResumeDispatchThread(u32 suspended); int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr); u32 sceKernelReferThreadStatus(u32 uid, u32 statusPtr); u32 sceKernelReferThreadRunStatus(u32 uid, u32 statusPtr); int sceKernelReleaseWaitThread(SceUID threadID); int sceKernelChangeCurrentThreadAttr(u32 clearAttr, u32 setAttr); int sceKernelRotateThreadReadyQueue(int priority); int KernelRotateThreadReadyQueue(int priority); int sceKernelCheckThreadStack(); int sceKernelSuspendThread(SceUID threadID); int sceKernelResumeThread(SceUID threadID); int sceKernelWakeupThread(SceUID threadID); int sceKernelCancelWakeupThread(SceUID threadID); int sceKernelSleepThread(); int sceKernelSleepThreadCB(); int sceKernelTerminateDeleteThread(int threadno); int sceKernelTerminateThread(SceUID threadID); int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr); int sceKernelGetThreadExitStatus(SceUID threadID); u32 sceKernelGetThreadmanIdType(u32); u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 idCountPtr); u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter); void __KernelScheduleWakeup(SceUID threadID, s64 usFromNow); struct SceKernelSysClock { u32_le lo; u32_le hi; }; // TODO: Map these to PSP wait types. Most of these are wrong. // remember to update the waitTypeNames array in sceKernelThread.cpp when changing these enum WaitType : int { WAITTYPE_NONE = 0, WAITTYPE_SLEEP = 1, WAITTYPE_DELAY = 2, WAITTYPE_SEMA = 3, WAITTYPE_EVENTFLAG = 4, WAITTYPE_MBX = 5, WAITTYPE_VPL = 6, WAITTYPE_FPL = 7, WAITTYPE_MSGPIPE = 8, // fake WAITTYPE_THREADEND = 9, WAITTYPE_AUDIOCHANNEL = 10, // this is fake, should be replaced with 8 eventflags ( ?? ) WAITTYPE_UMD = 11, // this is fake, should be replaced with 1 eventflag ( ?? ) WAITTYPE_VBLANK = 12, // fake WAITTYPE_MUTEX = 13, WAITTYPE_LWMUTEX = 14, WAITTYPE_CTRL = 15, WAITTYPE_IO = 16, WAITTYPE_GEDRAWSYNC = 17, WAITTYPE_GELISTSYNC = 18, WAITTYPE_MODULE = 19, WAITTYPE_HLEDELAY = 20, WAITTYPE_TLSPL = 21, WAITTYPE_VMEM = 22, WAITTYPE_ASYNCIO = 23, WAITTYPE_MICINPUT = 24, // fake WAITTYPE_NET = 25, // fake WAITTYPE_USB = 26, // fake WAITTYPE_PLUGIN = 27, // this is fake, for when LoadExec thread is waiting for plugins to finish loading NUM_WAITTYPES }; const char *WaitTypeToString(WaitType type); // Suspend wait and timeout while a thread enters a callback. typedef void (* WaitBeginCallbackFunc)(SceUID threadID, SceUID prevCallbackId); // Resume wait and timeout as a thread exits a callback. typedef void (* WaitEndCallbackFunc)(SceUID threadID, SceUID prevCallbackId); void __KernelRegisterWaitTypeFuncs(WaitType type, WaitBeginCallbackFunc beginFunc, WaitEndCallbackFunc endFunc); #if COMMON_LITTLE_ENDIAN typedef WaitType WaitType_le; #else typedef swap_struct_t > WaitType_le; #endif // Real PSP struct, don't change the fields. struct SceKernelThreadRunStatus { SceSize_le size; u32_le status; s32_le currentPriority; WaitType_le waitType; SceUID_le waitID; s32_le wakeupCount; SceKernelSysClock runForClocks; s32_le numInterruptPreempts; s32_le numThreadPreempts; s32_le numReleases; }; // Real PSP struct, don't change the fields. struct NativeThread { u32_le nativeSize; char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; // Threading stuff u32_le attr; u32_le status; u32_le entrypoint; u32_le initialStack; u32_le stackSize; u32_le gpreg; s32_le initialPriority; s32_le currentPriority; WaitType_le waitType; SceUID_le waitID; s32_le wakeupCount; s32_le exitStatus; SceKernelSysClock runForClocks; s32_le numInterruptPreempts; s32_le numThreadPreempts; s32_le numReleases; }; struct ThreadWaitInfo { u32 waitValue; u32 timeoutPtr; }; enum { PSP_THREAD_ATTR_KERNEL = 0x00001000, PSP_THREAD_ATTR_VFPU = 0x00004000, PSP_THREAD_ATTR_SCRATCH_SRAM = 0x00008000, // Save/restore scratch as part of context??? PSP_THREAD_ATTR_NO_FILLSTACK = 0x00100000, // No filling of 0xff. PSP_THREAD_ATTR_CLEAR_STACK = 0x00200000, // Clear thread stack when deleted. PSP_THREAD_ATTR_LOW_STACK = 0x00400000, // Allocate stack from bottom not top. PSP_THREAD_ATTR_USER = 0x80000000, PSP_THREAD_ATTR_USBWLAN = 0xa0000000, PSP_THREAD_ATTR_VSH = 0xc0000000, // TODO: Support more, not even sure what all of these mean. PSP_THREAD_ATTR_USER_MASK = 0xf8f060ff, PSP_THREAD_ATTR_USER_ERASE = 0x78800000, PSP_THREAD_ATTR_SUPPORTED = (PSP_THREAD_ATTR_KERNEL | PSP_THREAD_ATTR_VFPU | PSP_THREAD_ATTR_NO_FILLSTACK | PSP_THREAD_ATTR_CLEAR_STACK | PSP_THREAD_ATTR_LOW_STACK | PSP_THREAD_ATTR_USER) }; enum ThreadStatus : u32 { THREADSTATUS_RUNNING = 1, THREADSTATUS_READY = 2, THREADSTATUS_WAIT = 4, THREADSTATUS_SUSPEND = 8, THREADSTATUS_DORMANT = 16, THREADSTATUS_DEAD = 32, THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND }; const char *ThreadStatusToString(ThreadStatus status); class PSPThread : public KernelObject { public: PSPThread() : debug(&context) {} const char *GetName() override { return nt.name; } const char *GetTypeName() override { return GetStaticTypeName(); } static const char *GetStaticTypeName() { return "Thread"; } void GetQuickInfo(char *ptr, int size) override; static u32 GetMissingErrorCode(); static int GetStaticIDType() { return SCE_KERNEL_TMID_Thread; } int GetIDType() const override { return SCE_KERNEL_TMID_Thread; } bool AllocateStack(u32 &stackSize); bool FillStack(); void FreeStack(); bool PushExtendedStack(u32 size); bool PopExtendedStack(); // Can't use a destructor since savestates will call that too. void Cleanup(); BlockAllocator &StackAllocator(); void setReturnValue(u32 retval); void setReturnValue(u64 retval); void resumeFromWait(); bool isWaitingFor(WaitType type, int id) const; int getWaitID(WaitType type) const; ThreadWaitInfo getWaitInfo() const; // 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; } void DoState(PointerWrap &p) override; NativeThread nt{}; ThreadWaitInfo waitInfo{}; SceUID moduleId = -1; KernelThreadDebugInterface debug; bool isProcessingCallbacks = false; u32 currentMipscallId = -1; SceUID currentCallbackId = -1; PSPThreadContext context{}; std::vector callbacks; // TODO: Should probably just be a vector. std::list pendingMipsCalls; 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 pushedStacks; StackInfo currentStack{}; // For thread end. std::vector waitingThreads; // Key is the callback id it was for, or if no callback, the thread id. std::map pausedWaits; }; // Internal API, used by implementations of kernel functions void __KernelThreadingInit(); void __KernelThreadingDoState(PointerWrap &p); void __KernelThreadingDoStateLate(PointerWrap &p); void __KernelThreadingShutdown(); std::string __KernelThreadingSummary(); KernelObject *__KernelThreadObject(); KernelObject *__KernelCallbackObject(); SceUID __KernelGetCurThread(); int KernelCurThreadPriority(); bool KernelChangeThreadPriority(SceUID threadID, int priority); u32 __KernelGetCurThreadStack(); u32 __KernelGetCurThreadStackStart(); const char *__KernelGetThreadName(SceUID threadID); bool KernelIsThreadDormant(SceUID threadID); bool KernelIsThreadWaiting(SceUID threadID); void __KernelSaveContext(PSPThreadContext *ctx, bool vfpuEnabled); void __KernelLoadContext(const PSPThreadContext *ctx, bool vfpuEnabled); u32 __KernelResumeThreadFromWait(SceUID threadID, u32 retval); // can return an error value u32 __KernelResumeThreadFromWait(SceUID threadID, u64 retval); inline u32 __KernelResumeThreadFromWait(SceUID threadID, int retval) { return __KernelResumeThreadFromWait(threadID, (u32)retval); } inline u32 __KernelResumeThreadFromWait(SceUID threadID, s64 retval) { return __KernelResumeThreadFromWait(threadID, (u64)retval); } u32 __KernelGetWaitValue(SceUID threadID, u32 &error); u32 __KernelGetWaitTimeoutPtr(SceUID threadID, u32 &error); SceUID __KernelGetWaitID(SceUID threadID, WaitType type, u32 &error); SceUID __KernelGetCurrentCallbackID(SceUID threadID, u32 &error); void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, u32 timeoutPtr, bool processCallbacks, const char *reason); void __KernelWaitCallbacksCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 timeoutPtr); void __KernelReSchedule(const char *reason = "no reason"); void __KernelReSchedule(bool doCallbacks, const char *reason); SceUID __KernelGetCurThread(); SceUID __KernelGetCurThreadModuleId(); SceUID __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution void __KernelStartIdleThreads(SceUID moduleId); void __KernelReturnFromThread(); // Called as HLE function u32 __KernelGetThreadPrio(SceUID id); bool __KernelThreadSortPriority(SceUID thread1, SceUID thread2); bool __KernelIsDispatchEnabled(); void __KernelReturnFromExtendStack(); void __KernelIdle(); u32 HLEMipsCallReturnAddress(); u32 __KernelCallbackReturnAddress(); u32 __KernelInterruptReturnAddress(); // TODO: remove SceUID sceKernelCreateCallback(const char *name, u32 entrypoint, u32 signalArg); int sceKernelDeleteCallback(SceUID cbId); int sceKernelNotifyCallback(SceUID cbId, int notifyArg); int sceKernelCancelCallback(SceUID cbId); int sceKernelGetCallbackCount(SceUID cbId); void sceKernelCheckCallback(); int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr); class PSPAction; // Not an official Callback object, just calls a mips function on the current thread. // Takes ownership of afterAction. void __KernelDirectMipsCall(u32 entryPoint, PSPAction *afterAction, u32 args[], int numargs, bool reschedAfter); void __KernelReturnFromMipsCall(); // Called as HLE function bool __KernelInCallback(); // Should be called by (nearly) all ...CB functions. bool __KernelCheckCallbacks(); bool __KernelForceCallbacks(); bool __KernelCurHasReadyCallbacks(); void __KernelSwitchContext(PSPThread *target, const char *reason); bool __KernelExecutePendingMipsCalls(PSPThread *currentThread, bool reschedAfter); void __KernelNotifyCallback(SceUID cbId, int notifyArg); // Switch to an idle / non-user thread, if not already on one. // Returns whether a switch occurred. bool __KernelSwitchOffThread(const char *reason); bool __KernelSwitchToThread(SceUID threadID, const char *reason); // Set a thread's return address to a specific FakeSyscall nid. // Discards old RA. Only useful for special threads that do special things on exit. u32 __KernelSetThreadRA(SceUID threadID, u32 nid); // A call into game code. These can be pending on a thread. // Similar to Callback-s (NOT CallbackInfos) in JPCSP. typedef PSPAction *(*ActionCreator)(); PSPAction *__KernelCreateAction(int actionType); int __KernelRegisterActionType(ActionCreator creator); void __KernelRestoreActionType(int actionType, ActionCreator creator); struct MipsCall { MipsCall() { doAfter = nullptr; } u32 entryPoint; u32 cbId; u32 args[6]; int numArgs; PSPAction *doAfter; u32 savedPc; u32 savedV0; u32 savedV1; std::string tag; u32 savedId; bool reschedAfter; void DoState(PointerWrap &p); void setReturnValue(u32 value); void setReturnValue(u64 value); inline void setReturnValue(int value) { setReturnValue((u32)value); } inline void setReturnValue(s64 value) { setReturnValue((u64)value); } }; class PSPAction { public: virtual ~PSPAction() {} virtual void run(MipsCall &call) = 0; virtual void DoState(PointerWrap &p) = 0; int actionTypeID; }; void __KernelChangeThreadState(PSPThread *thread, ThreadStatus newStatus); typedef void (*ThreadCallback)(SceUID threadID); void __KernelListenThreadEnd(ThreadCallback callback); struct DebugThreadInfo { SceUID id; char name[KERNELOBJECT_MAX_NAME_LENGTH+1]; ThreadStatus status; u32 curPC; u32 entrypoint; u32 initialStack; int stackSize; int priority; WaitType waitType; SceUID waitID; bool isCurrent; }; std::vector GetThreadsInfo(); DebugInterface *KernelDebugThread(SceUID threadID); void __KernelChangeThreadState(SceUID threadId, ThreadStatus newStatus); int LoadExecForUser_362A956B(); int sceKernelRegisterExitCallback(SceUID cbId); KernelObject *__KernelThreadEventHandlerObject(); SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg); int sceKernelReleaseThreadEventHandler(SceUID uid); int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr);