From 04576189b4a3f3cd98d62008fb98954306e5203a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 May 2016 21:15:25 -0700 Subject: [PATCH 1/7] Callbacks: Fix delete when multiple for a thread. Oops, this was deleting all after the deleted one. --- Core/HLE/sceKernelThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index d125ec2254..589ac0fbfe 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2716,7 +2716,7 @@ int sceKernelDeleteCallback(SceUID cbId) { Thread *thread = kernelObjects.Get(cb->nc.threadId, error); if (thread) - thread->callbacks.erase(std::find(thread->callbacks.begin(), thread->callbacks.end(), cbId), thread->callbacks.end()); + thread->callbacks.erase(std::remove(thread->callbacks.begin(), thread->callbacks.end(), cbId), thread->callbacks.end()); if (cb->nc.notifyCount != 0) readyCallbacksCount--; From e9916bdf0b255b95dbb0c830f4e6e4bca7908021 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 4 Jan 2014 22:41:37 -0800 Subject: [PATCH 2/7] ThreadEvent: Add management functions. Currently never being triggered, though. --- Core/HLE/sceKernel.cpp | 9 ++-- Core/HLE/sceKernelThread.cpp | 89 ++++++++++++++++++++++++++++++++++++ Core/HLE/sceKernelThread.h | 5 ++ 3 files changed, 99 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index c5b7b85e71..8e9bea0b5b 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -596,6 +596,8 @@ KernelObject *KernelObjectPool::CreateByIDType(int type) { return __KernelFileNodeObject(); case PPSSPP_KERNEL_TMID_DirList: return __KernelDirListingObject(); + case SCE_KERNEL_TMID_ThreadEventHandler: + return __KernelThreadEventHandlerObject(); default: ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type); @@ -786,10 +788,6 @@ const HLEFunction ThreadManForUser[] = {0X278C0DF5, &WrapI_IU, "sceKernelWaitThreadEnd", 'i', "ix" }, {0XD59EAD2F, &WrapI_I, "sceKernelWakeupThread", 'i', "i" }, //AI Go, audio? - {0X0C106E53, nullptr, "sceKernelRegisterThreadEventHandler", '?', "" }, - {0X72F3C145, nullptr, "sceKernelReleaseThreadEventHandler", '?', "" }, - {0X369EEB6B, nullptr, "sceKernelReferThreadEventHandlerStatus", '?', "" }, - {0x349d6d6c, &sceKernelCheckCallback, "sceKernelCheckCallback", 'i', "" }, {0XE81CAF8F, &WrapI_CUU, "sceKernelCreateCallback", 'i', "sxx" }, {0XEDBA5844, &WrapI_I, "sceKernelDeleteCallback", 'i', "i" }, @@ -864,6 +862,9 @@ const HLEFunction ThreadManForUser[] = {0x0E927AED, &_sceKernelReturnFromTimerHandler, "_sceKernelReturnFromTimerHandler", 'v', "" }, {0X532A522E, &WrapV_I<_sceKernelExitThread>, "_sceKernelExitThread", 'v', "i" }, + {0x0C106E53, &WrapI_CIUUU, "sceKernelRegisterThreadEventHandler", 'i', "sixxx", }, + {0x72F3C145, &WrapI_I, "sceKernelReleaseThreadEventHandler", 'i', "i" }, + {0x369EEB6B, &WrapI_IU, "sceKernelReferThreadEventHandlerStatus", 'i', "ip" }, // Shouldn't hook this up. No games should import this function manually and call it. // {0x6E9EA350, _sceKernelReturnFromCallback,"_sceKernelReturnFromCallback"}, diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 589ac0fbfe..d5414420e4 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -3470,3 +3470,92 @@ int LoadExecForUser_362A956B() Memory::Write_U32(-1, parameterArea + 8); return 0; } + +static const SceUID SCE_TE_THREADID_ALL_USER = 0xFFFFFFF0; +enum { + THREADEVENT_CREATE = 1, + THREADEVENT_START = 2, + THREADEVENT_EXIT = 4, + THREADEVENT_DELETE = 8, + THREADEVENT_KNOWN = THREADEVENT_CREATE | THREADEVENT_START | THREADEVENT_EXIT | THREADEVENT_DELETE, +}; + +struct NativeThreadEventHandler { + u32 size; + char name[KERNELOBJECT_MAX_NAME_LENGTH + 1]; + SceUID threadID; + u32 mask; + u32 handlerPtr; + u32 commonArg; +}; + +struct ThreadEventHandler : public KernelObject { + const char *GetName() { return nteh.name; } + const char *GetTypeName() { return "ThreadEventHandler"; } + static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_TEID; } + static int GetStaticIDType() { return SCE_KERNEL_TMID_ThreadEventHandler; } + int GetIDType() const { return SCE_KERNEL_TMID_ThreadEventHandler; } + + virtual void DoState(PointerWrap &p) { + auto s = p.Section("ThreadEventHandler", 1); + if (!s) + return; + + p.Do(nteh); + } + + NativeThreadEventHandler nteh; +}; + +KernelObject *__KernelThreadEventHandlerObject() { + // Default object to load from state. + return new ThreadEventHandler; +} + +SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg) { + if (!name) { + return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ERROR, "invalid name"); + } + if (threadID == 0) { + return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid thread id"); + } + u32 error; + if (kernelObjects.Get(threadID, error) == NULL && threadID != SCE_TE_THREADID_ALL_USER) { + return hleReportError(SCEKERNEL, error, "bad thread id"); + } + if ((mask & ~THREADEVENT_KNOWN) != 0) { + return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_MASK, "invalid event mask"); + } + + auto teh = new ThreadEventHandler; + teh->nteh.size = sizeof(teh->nteh); + strncpy(teh->nteh.name, name, KERNELOBJECT_MAX_NAME_LENGTH); + teh->nteh.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; + teh->nteh.threadID = threadID; + teh->nteh.mask = mask; + teh->nteh.handlerPtr = handlerPtr; + teh->nteh.commonArg = commonArg; + + SceUID uid = kernelObjects.Create(teh); + + return hleLogSuccessI(SCEKERNEL, uid); +} + +int sceKernelReleaseThreadEventHandler(SceUID uid) { + return hleLogSuccessI(SCEKERNEL, kernelObjects.Destroy(uid)); +} + +int sceKernelReferThreadEventHandlerStatus(SceUID uid, u32 infoPtr) { + u32 error; + auto teh = kernelObjects.Get(uid, error); + if (!teh) { + return hleReportError(SCEKERNEL, error, "bad handler id"); + } + + if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) { + Memory::WriteStruct(infoPtr, &teh->nteh); + return hleLogSuccessI(SCEKERNEL, 0); + } else { + return hleLogDebug(SCEKERNEL, 0, "struct size was 0"); + } +} diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 74ec6cd19e..8f02e17719 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -320,3 +320,8 @@ 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); From be1cde15ba045f486bbefe4f46031b86e1b9ba11 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 5 Jan 2014 20:20:56 -0800 Subject: [PATCH 3/7] ThreadEvent: Initial support for create/delete. --- Core/HLE/sceKernelThread.cpp | 75 +++++++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index d5414420e4..e8b08482fa 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -91,7 +91,17 @@ const char *getWaitTypeName(WaitType type) return "Unknown"; } - enum { +enum ThreadEventType { + THREADEVENT_CREATE = 1, + THREADEVENT_START = 2, + THREADEVENT_EXIT = 4, + THREADEVENT_DELETE = 8, + THREADEVENT_SUPPORTED = THREADEVENT_CREATE | THREADEVENT_START | THREADEVENT_EXIT | THREADEVENT_DELETE, +}; + +void __KernelThreadTriggerEvent(bool isKernel, SceUID threadID, ThreadEventType type); + +enum { PSP_THREAD_ATTR_KERNEL = 0x00001000, PSP_THREAD_ATTR_VFPU = 0x00004000, PSP_THREAD_ATTR_SCRATCH_SRAM = 0x00008000, // Save/restore scratch as part of context??? @@ -632,6 +642,9 @@ u32 extendReturnHackAddr; u32 moduleReturnHackAddr; std::vector threadEndListeners; +typedef std::vector ThreadEventHandlerList; +static std::map threadEventHandlers; + // Lists all thread ids that aren't deleted/etc. std::vector threadqueue; @@ -943,7 +956,7 @@ void __KernelThreadingInit() void __KernelThreadingDoState(PointerWrap &p) { - auto s = p.Section("sceKernelThread", 1); + auto s = p.Section("sceKernelThread", 1, 2); if (!s) return; @@ -978,6 +991,9 @@ void __KernelThreadingDoState(PointerWrap &p) __SetCurrentThread(kernelObjects.GetFast(currentThread), currentThread, __KernelGetThreadName(currentThread)); lastSwitchCycles = CoreTiming::GetTicks(); + + if (s >= 2) + p.Do(threadEventHandlers); } void __KernelThreadingDoStateLate(PointerWrap &p) @@ -1146,6 +1162,7 @@ void __KernelThreadingShutdown() __SetCurrentThread(NULL, 0, NULL); intReturnHackAddr = 0; pausedDelays.clear(); + threadEventHandlers.clear(); } const char *__KernelGetThreadName(SceUID threadID) @@ -1633,6 +1650,9 @@ u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason) t->Cleanup(); } + // TODO: Thread should not be deleted yet... + __KernelThreadTriggerEvent((t->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, threadID, THREADEVENT_DELETE); + return kernelObjects.Destroy(threadID); } @@ -1940,6 +1960,8 @@ int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 // 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. hleReSchedule("thread created"); + + __KernelThreadTriggerEvent((attr & PSP_THREAD_ATTR_KERNEL) != 0, id, THREADEVENT_CREATE); return hleLogSuccessInfoI(SCEKERNEL, id); } @@ -3472,13 +3494,6 @@ int LoadExecForUser_362A956B() } static const SceUID SCE_TE_THREADID_ALL_USER = 0xFFFFFFF0; -enum { - THREADEVENT_CREATE = 1, - THREADEVENT_START = 2, - THREADEVENT_EXIT = 4, - THREADEVENT_DELETE = 8, - THREADEVENT_KNOWN = THREADEVENT_CREATE | THREADEVENT_START | THREADEVENT_EXIT | THREADEVENT_DELETE, -}; struct NativeThreadEventHandler { u32 size; @@ -3512,6 +3527,37 @@ KernelObject *__KernelThreadEventHandlerObject() { return new ThreadEventHandler; } +void __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID threadID, ThreadEventType type) { + for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) { + u32 error; + const auto teh = kernelObjects.Get(*it, error); + if (!teh || (teh->nteh.mask & type) == 0) { + continue; + } + + const u32 args[] = {(u32)type, (u32)threadID, teh->nteh.commonArg}; + __KernelCallAddress(__GetCurrentThread(), teh->nteh.handlerPtr, nullptr, args, ARRAY_SIZE(args), true, 0); + } +} + +void __KernelThreadTriggerEvent(bool isKernel, SceUID threadID, ThreadEventType type) { + auto exactHandlers = threadEventHandlers.find(threadID); + if (exactHandlers != threadEventHandlers.end()) { + __KernelThreadTriggerEvent(exactHandlers->second, threadID, type); + } + if (isKernel) { + auto kernelHandlers = threadEventHandlers.find(SCE_TE_THREADID_ALL_USER); + if (kernelHandlers != threadEventHandlers.end()) { + __KernelThreadTriggerEvent(kernelHandlers->second, threadID, type); + } + } else { + auto userHandlers = threadEventHandlers.find(SCE_TE_THREADID_ALL_USER); + if (userHandlers != threadEventHandlers.end()) { + __KernelThreadTriggerEvent(userHandlers->second, threadID, type); + } + } +} + SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u32 mask, u32 handlerPtr, u32 commonArg) { if (!name) { return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ERROR, "invalid name"); @@ -3523,7 +3569,7 @@ SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u3 if (kernelObjects.Get(threadID, error) == NULL && threadID != SCE_TE_THREADID_ALL_USER) { return hleReportError(SCEKERNEL, error, "bad thread id"); } - if ((mask & ~THREADEVENT_KNOWN) != 0) { + if ((mask & ~THREADEVENT_SUPPORTED) != 0) { return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_MASK, "invalid event mask"); } @@ -3537,11 +3583,20 @@ SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u3 teh->nteh.commonArg = commonArg; SceUID uid = kernelObjects.Create(teh); + threadEventHandlers[threadID].push_back(uid); return hleLogSuccessI(SCEKERNEL, uid); } int sceKernelReleaseThreadEventHandler(SceUID uid) { + u32 error; + auto teh = kernelObjects.Get(uid, error); + if (!teh) { + return hleReportError(SCEKERNEL, error, "bad handler id"); + } + + auto &handlers = threadEventHandlers[teh->nteh.threadID]; + handlers.erase(std::remove(handlers.begin(), handlers.end(), uid), handlers.end()); return hleLogSuccessI(SCEKERNEL, kernelObjects.Destroy(uid)); } From ceb36fc81de53315c222c300154fabc7c4dd3a52 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 May 2016 16:17:33 -0700 Subject: [PATCH 4/7] ThreadEvent: Allow an exit event handler on self. --- Core/HLE/sceKernelThread.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index e8b08482fa..83149b4104 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -3563,7 +3563,10 @@ SceUID sceKernelRegisterThreadEventHandler(const char *name, SceUID threadID, u3 return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ERROR, "invalid name"); } if (threadID == 0) { - return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid thread id"); + // "atexit"? + if (mask != THREADEVENT_EXIT) { + return hleReportError(SCEKERNEL, SCE_KERNEL_ERROR_ILLEGAL_ATTR, "invalid thread id"); + } } u32 error; if (kernelObjects.Get(threadID, error) == NULL && threadID != SCE_TE_THREADID_ALL_USER) { From c6cd614f2be2d86bb39d292c90d0ca46273eeb94 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 May 2016 16:59:19 -0700 Subject: [PATCH 5/7] Thread: Allow mipscalls to be scheduled in a row. Should be fine to just use the stack to save these things, likely that's what happens in real firmware. This fixes issues when a second mipscall is scheduled in the same HLE syscall. --- Core/HLE/sceKernelThread.cpp | 76 ++++++++++++++++++++++++------------ Core/HLE/sceKernelThread.h | 1 - 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 83149b4104..f8eaac9d7c 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -159,21 +159,16 @@ public: return; p.Do(nc); - p.Do(savedPC); - p.Do(savedRA); - p.Do(savedV0); - p.Do(savedV1); - // No longer used. - u32 legacySavedIdRegister = 0; - p.Do(legacySavedIdRegister); + // Saved values were moved to mips call, ignoring here. + u32 legacySaved = 0; + p.Do(legacySaved); + p.Do(legacySaved); + p.Do(legacySaved); + p.Do(legacySaved); + p.Do(legacySaved); } NativeCallback nc; - - u32 savedPC; - u32 savedRA; - u32 savedV0; - u32 savedV1; }; #if COMMON_LITTLE_ENDIAN @@ -617,7 +612,7 @@ struct WaitTypeFuncs WaitEndCallbackFunc endFunc; }; -void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter); +bool __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter); Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr); void __KernelResetThread(Thread *t, int lowestPriority); @@ -703,7 +698,8 @@ void MipsCall::DoState(PointerWrap &p) // No longer used. u32 legacySavedIdRegister = 0; p.Do(legacySavedIdRegister); - p.Do(savedRa); + u32 legacySavedRa = 0; + p.Do(legacySavedRa); p.Do(savedPc); p.Do(savedV0); p.Do(savedV1); @@ -1651,6 +1647,8 @@ u32 __KernelDeleteThread(SceUID threadID, int exitStatus, const char *reason) } // TODO: Thread should not be deleted yet... + // Before triggering, set v0. It'll be restored if one is called. + RETURN(error); __KernelThreadTriggerEvent((t->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, threadID, THREADEVENT_DELETE); return kernelObjects.Destroy(threadID); @@ -1961,6 +1959,8 @@ int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. hleReSchedule("thread created"); + // Before triggering, set v0, since we restore on return. + RETURN(id); __KernelThreadTriggerEvent((attr & PSP_THREAD_ATTR_KERNEL) != 0, id, THREADEVENT_CREATE); return hleLogSuccessInfoI(SCEKERNEL, id); } @@ -3025,7 +3025,6 @@ static bool __CanExecuteCallbackNow(Thread *thread) { void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, const u32 args[], int numargs, bool reschedAfter, SceUID cbId) { - hleSkipDeadbeef(); _dbg_assert_msg_(SCEKERNEL, numargs <= 6, "MipsCalls can only take 6 args."); if (thread) { @@ -3075,8 +3074,7 @@ void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, co if (__CanExecuteCallbackNow(thread)) { thread = __GetCurrentThread(); __KernelChangeThreadState(thread, THREADSTATUS_RUNNING); - __KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter); - called = true; + called = __KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter); } } @@ -3095,13 +3093,14 @@ void __KernelDirectMipsCall(u32 entryPoint, Action *afterAction, u32 args[], int __KernelCallAddress(__GetCurrentThread(), entryPoint, afterAction, args, numargs, reschedAfter, 0); } -void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter) +bool __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter) { + hleSkipDeadbeef(); + Thread *cur = __GetCurrentThread(); - if (cur == NULL) - { + if (cur == nullptr) { ERROR_LOG(SCEKERNEL, "__KernelExecuteMipsCallOnCurrentThread(): Bad current thread"); - return; + return false; } if (g_inCbCount > 0) { @@ -3110,9 +3109,24 @@ void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter) DEBUG_LOG(SCEKERNEL, "Executing mipscall %i", callId); MipsCall *call = mipsCalls.get(callId); + // Grab some MIPS stack space. + u32 &sp = currentMIPS->r[MIPS_REG_SP]; + if (!Memory::IsValidAddress(sp - 32 * 4)) { + ERROR_LOG_REPORT(SCEKERNEL, "__KernelExecuteMipsCallOnCurrentThread(): Not enough free stack"); + return false; + } + + // Let's just save regs generously. Better to be safe. + sp -= 32 * 4; + for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; ++i) { + Memory::Write_U32(currentMIPS->r[i], sp + i * 4); + } + Memory::Write_U32(currentMIPS->r[MIPS_REG_T8], sp + MIPS_REG_T8 * 4); + Memory::Write_U32(currentMIPS->r[MIPS_REG_T9], sp + MIPS_REG_T9 * 4); + Memory::Write_U32(currentMIPS->r[MIPS_REG_RA], sp + MIPS_REG_RA * 4); + // Save the few regs that need saving call->savedPc = currentMIPS->pc; - call->savedRa = currentMIPS->r[MIPS_REG_RA]; call->savedV0 = currentMIPS->r[MIPS_REG_V0]; call->savedV1 = currentMIPS->r[MIPS_REG_V1]; call->savedId = cur->currentMipscallId; @@ -3129,6 +3143,8 @@ void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter) if (call->cbId != 0) g_inCbCount++; currentCallbackThreadID = currentThread; + + return true; } void __KernelReturnFromMipsCall() @@ -3156,8 +3172,17 @@ void __KernelReturnFromMipsCall() delete call->doAfter; } + u32 &sp = currentMIPS->r[MIPS_REG_SP]; + for (int i = MIPS_REG_A0; i <= MIPS_REG_T7; ++i) { + currentMIPS->r[i] = Memory::Read_U32(sp + i * 4); + } + currentMIPS->r[MIPS_REG_T8] = Memory::Read_U32(sp + MIPS_REG_T8 * 4); + currentMIPS->r[MIPS_REG_T9] = Memory::Read_U32(sp + MIPS_REG_T9 * 4); + currentMIPS->r[MIPS_REG_RA] = Memory::Read_U32(sp + MIPS_REG_RA * 4); + sp += 32 * 4; + currentMIPS->pc = call->savedPc; - currentMIPS->r[MIPS_REG_RA] = call->savedRa; + // This is how we set the return value. currentMIPS->r[MIPS_REG_V0] = call->savedV0; currentMIPS->r[MIPS_REG_V1] = call->savedV1; cur->currentMipscallId = call->savedId; @@ -3206,8 +3231,9 @@ bool __KernelExecutePendingMipsCalls(Thread *thread, bool reschedAfter) // Pop off the first pending mips call u32 callId = thread->pendingMipsCalls.front(); thread->pendingMipsCalls.pop_front(); - __KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter); - return true; + if (__KernelExecuteMipsCallOnCurrentThread(callId, reschedAfter)) { + return true; + } } return false; } diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 8f02e17719..0e92d2d369 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -254,7 +254,6 @@ struct MipsCall { u32 args[6]; int numArgs; Action *doAfter; - u32 savedRa; u32 savedPc; u32 savedV0; u32 savedV1; From 97cce6ccfe01c759cb89c2433d61977fa4dd9296 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 28 May 2016 21:14:19 -0700 Subject: [PATCH 6/7] ThreadEvent: Trigger handlers for start/exit too. Start isn't in the right place or on the right thread, and exit isn't on the right thread either. But these don't seem very particular about what thread they run on. --- Core/HLE/ThreadQueueList.h | 11 +++++++++ Core/HLE/sceKernelThread.cpp | 48 +++++++++++++++++++++++++++++++++--- Core/HLE/sceKernelThread.h | 1 + 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/Core/HLE/ThreadQueueList.h b/Core/HLE/ThreadQueueList.h index 9c1b1e5da2..beb149657a 100644 --- a/Core/HLE/ThreadQueueList.h +++ b/Core/HLE/ThreadQueueList.h @@ -99,6 +99,17 @@ struct ThreadQueueList { return 0; } + inline SceUID peek_first(u32 priority) { + Queue *cur = first; + while (cur != invalid()) { + if (cur->size() > 0) + return cur->data[cur->first]; + cur = cur->next; + } + + return 0; + } + inline void push_front(u32 priority, const SceUID threadID) { Queue *cur = &queues[priority]; cur->data[--cur->first] = threadID; diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index f8eaac9d7c..06fb085d72 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -2010,6 +2010,11 @@ int __KernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr, bo dispatchEnabled = true; __KernelChangeReadyState(startThread, threadToStartID, true); + + // Need to write out v0 before triggering event. + // TODO: Technically the wrong place. This should trigger when the thread actually starts (e.g. if suspended.) + RETURN(0); + __KernelThreadTriggerEvent((startThread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, threadToStartID, THREADEVENT_START); return 0; } @@ -2077,6 +2082,9 @@ void __KernelReturnFromThread() hleReSchedule("thread returned"); + // TODO: This should trigger ON the thread when it exits. + __KernelThreadTriggerEvent((thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, thread->GetUID(), THREADEVENT_EXIT); + // The stack will be deallocated when the thread is deleted. } @@ -2090,6 +2098,9 @@ void sceKernelExitThread(int exitStatus) hleReSchedule("thread exited"); + // TODO: This should trigger ON the thread when it exits. + __KernelThreadTriggerEvent((thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, thread->GetUID(), THREADEVENT_EXIT); + // The stack will be deallocated when the thread is deleted. } @@ -2103,6 +2114,9 @@ void _sceKernelExitThread(int exitStatus) hleReSchedule("thread _exited"); + // TODO: This should trigger ON the thread when it exits. + __KernelThreadTriggerEvent((thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, thread->GetUID(), THREADEVENT_EXIT); + // The stack will be deallocated when the thread is deleted. } @@ -2117,6 +2131,9 @@ void sceKernelExitDeleteThread(int exitStatus) g_inCbCount = 0; hleReSchedule("thread exited with delete"); + + // TODO: This should trigger ON the thread when it exits. + __KernelThreadTriggerEvent((thread->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, thread->GetUID(), THREADEVENT_EXIT); } else ERROR_LOG_REPORT(SCEKERNEL, "sceKernelExitDeleteThread(%d) ERROR - could not find myself!", exitStatus); @@ -2230,9 +2247,17 @@ int sceKernelTerminateDeleteThread(int threadID) Thread *t = kernelObjects.Get(threadID, error); if (t) { + bool wasStopped = t->isStopped(); + INFO_LOG(SCEKERNEL, "sceKernelTerminateDeleteThread(%i)", threadID); error = __KernelDeleteThread(threadID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "thread terminated with delete"); + if (!wasStopped) { + // Set v0 before calling the handler, or it'll get lost. + RETURN(error); + __KernelThreadTriggerEvent((t->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, t->GetUID(), THREADEVENT_EXIT); + } + return error; } else @@ -2263,6 +2288,10 @@ int sceKernelTerminateThread(SceUID threadID) { // On terminate, we reset the thread priority. On exit, we don't always (see __KernelResetThread.) t->nt.currentPriority = t->nt.initialPriority; + // Need to set v0 since it'll be restored. + RETURN(0); + __KernelThreadTriggerEvent((t->nt.attr & PSP_THREAD_ATTR_KERNEL) != 0, t->GetUID(), THREADEVENT_EXIT); + return hleLogSuccessInfoI(SCEKERNEL, 0); } else { return hleLogError(SCEKERNEL, error, "thread doesn't exist"); @@ -2864,8 +2893,11 @@ void ActionAfterMipsCall::run(MipsCall &call) { u32 error; Thread *thread = kernelObjects.Get(threadID, error); if (thread) { - __KernelChangeReadyState(thread, threadID, (status & THREADSTATUS_READY) != 0); - thread->nt.status = status; + // Resume waiting after a callback, but not from terminate/delete. + if ((thread->nt.status & (THREADSTATUS_DEAD | THREADSTATUS_DORMANT)) == 0) { + __KernelChangeReadyState(thread, threadID, (status & THREADSTATUS_READY) != 0); + thread->nt.status = status; + } thread->nt.waitType = waitType; thread->nt.waitID = waitID; thread->waitInfo = waitInfo; @@ -3025,6 +3057,10 @@ static bool __CanExecuteCallbackNow(Thread *thread) { void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, const u32 args[], int numargs, bool reschedAfter, SceUID cbId) { + if (thread->isStopped()) { + WARN_LOG_REPORT(SCEKERNEL, "Running mipscall on dormant thread"); + } + _dbg_assert_msg_(SCEKERNEL, numargs <= 6, "MipsCalls can only take 6 args."); if (thread) { @@ -3554,6 +3590,12 @@ KernelObject *__KernelThreadEventHandlerObject() { } void __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID threadID, ThreadEventType type) { + Thread *thread = __GetCurrentThread(); + if (thread->isStopped()) { + SceUID nextThreadID = threadReadyQueue.peek_first(thread->nt.currentPriority); + thread = kernelObjects.GetFast(nextThreadID); + } + for (auto it = handlers.begin(), end = handlers.end(); it != end; ++it) { u32 error; const auto teh = kernelObjects.Get(*it, error); @@ -3562,7 +3604,7 @@ void __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID t } const u32 args[] = {(u32)type, (u32)threadID, teh->nteh.commonArg}; - __KernelCallAddress(__GetCurrentThread(), teh->nteh.handlerPtr, nullptr, args, ARRAY_SIZE(args), true, 0); + __KernelCallAddress(thread, teh->nteh.handlerPtr, nullptr, args, ARRAY_SIZE(args), true, 0); } } diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index 0e92d2d369..53fa5e4367 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -44,6 +44,7 @@ void 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 sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr); From 4fc12cdd2992c42dd35fe2b9107afcf17d30f288 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 29 May 2016 00:12:36 -0700 Subject: [PATCH 7/7] ThreadEvent: Handle getting scheduled off a thread. --- Core/HLE/ThreadQueueList.h | 2 +- Core/HLE/sceKernelThread.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/HLE/ThreadQueueList.h b/Core/HLE/ThreadQueueList.h index beb149657a..9111fff41d 100644 --- a/Core/HLE/ThreadQueueList.h +++ b/Core/HLE/ThreadQueueList.h @@ -99,7 +99,7 @@ struct ThreadQueueList { return 0; } - inline SceUID peek_first(u32 priority) { + inline SceUID peek_first() { Queue *cur = first; while (cur != invalid()) { if (cur->size() > 0) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 06fb085d72..978defe8a3 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -3057,7 +3057,7 @@ static bool __CanExecuteCallbackNow(Thread *thread) { void __KernelCallAddress(Thread *thread, u32 entryPoint, Action *afterAction, const u32 args[], int numargs, bool reschedAfter, SceUID cbId) { - if (thread->isStopped()) { + if (!thread || thread->isStopped()) { WARN_LOG_REPORT(SCEKERNEL, "Running mipscall on dormant thread"); } @@ -3591,8 +3591,8 @@ KernelObject *__KernelThreadEventHandlerObject() { void __KernelThreadTriggerEvent(const ThreadEventHandlerList &handlers, SceUID threadID, ThreadEventType type) { Thread *thread = __GetCurrentThread(); - if (thread->isStopped()) { - SceUID nextThreadID = threadReadyQueue.peek_first(thread->nt.currentPriority); + if (!thread || thread->isStopped()) { + SceUID nextThreadID = threadReadyQueue.peek_first(); thread = kernelObjects.GetFast(nextThreadID); }