diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index efc63c1b85..5a002a3f90 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -884,8 +884,7 @@ const HLEFunction ThreadManForUser[] = {0X721067F3, &WrapI_IU, "sceKernelReferTlsplStatus", 'i', "xp" }, // Not completely certain about args. {0X4A719FB2, &WrapI_I, "sceKernelFreeTlspl", 'i', "i" }, - // Internal. Takes (uid, &addr) as parameters... probably. - //{0x65F54FFB, nullptr, "_sceKernelAllocateTlspl", 'v', "" }, + // _sceKernelAllocateTlspl is added at the end of the table. // NOTE: sceKernelGetTlsAddr is in Kernel_Library, see sceKernelInterrupt.cpp. // Not sure if these should be hooked up. See below. @@ -899,6 +898,8 @@ const HLEFunction ThreadManForUser[] = {0X91E4F6A7, &WrapU_V, "sceKernelLibcClock", 'x', "" }, {0XB435DEC5, &WrapI_V, "sceKernelDcacheWritebackInvalidateAll", 'i', "" }, + // Internal, the syscall behind sceKernelGetTlsAddr in usersystemlib. + {0x65F54FFB, &WrapI_IUU<_sceKernelAllocateTlspl>, "_sceKernelAllocateTlspl", 'i', "ixx" }, }; const HLEFunction ThreadManForKernel[] = diff --git a/Core/HLE/sceKernelMemory.cpp b/Core/HLE/sceKernelMemory.cpp index 135fc808bf..281d505c5a 100644 --- a/Core/HLE/sceKernelMemory.cpp +++ b/Core/HLE/sceKernelMemory.cpp @@ -43,6 +43,8 @@ #include "Core/HLE/KernelWaitHelpers.h" const int TLSPL_NUM_INDEXES = 16; +// Wait value of a thread waiting in sceKernelGetTlsAddr. In _sceKernelAllocateTlspl it's the address pointer instead. +const u32 TLSPL_WAITVALUE_RETURN_ADDR = 1; ////////////////////////////////////////////////////////////////////////// // STATE BEGIN @@ -1823,7 +1825,15 @@ int __KernelFreeTls(TLSPL *tls, SceUID threadID) // Otherwise, if there was a thread waiting, we were full, so this newly freed one is theirs. tls->usage[freeBlock] = waitingThreadID; - __KernelResumeThreadFromWait(waitingThreadID, freedAddress); + // _sceKernelAllocateTlspl waits with its address pointer as the wait value, sceKernelGetTlsAddr with 1. + u32 error; + u32 addrPtr = __KernelGetWaitValue(waitingThreadID, error); + if (addrPtr != TLSPL_WAITVALUE_RETURN_ADDR) { + Memory::WriteOrException_U32(freedAddress, addrPtr); + __KernelResumeThreadFromWait(waitingThreadID, 0); + } else { + __KernelResumeThreadFromWait(waitingThreadID, freedAddress); + } // Gotta watch the thread to quit as well, since they've allocated now. tlsplThreadEndChecks.emplace(waitingThreadID, uid); @@ -1987,8 +1997,12 @@ int sceKernelDeleteTlspl(SceUID uid) WARN_LOG(Log::sceKernel, "sceKernelDeleteTlspl(%08x)", uid); - for (SceUID threadID : tls->waitingThreads) - HLEKernel::ResumeFromWait(threadID, WAITTYPE_TLSPL, uid, 0); + for (SceUID threadID : tls->waitingThreads) { + // sceKernelGetTlsAddr returns a null address, _sceKernelAllocateTlspl an error. + u32 error; + u32 result = __KernelGetWaitValue(threadID, error) != TLSPL_WAITVALUE_RETURN_ADDR ? SCE_KERNEL_ERROR_WAIT_DELETE : 0; + HLEKernel::ResumeFromWait(threadID, WAITTYPE_TLSPL, uid, result); + } hleReSchedule("deleted tlspl"); BlockAllocator *allocator = BlockAllocatorFromAddr(tls->address); @@ -2008,36 +2022,30 @@ struct FindTLSByIndexArg { TLSPL *result = nullptr; }; -int sceKernelGetTlsAddr(SceUID uid) { - if (!__KernelIsDispatchEnabled() || __IsInInterrupt()) - return hleLogWarning(Log::sceKernel, 0, "dispatch disabled"); - +static TLSPL *__KernelFindTlspl(SceUID uid) { u32 error; TLSPL *tls = kernelObjects.Get(uid, error); - if (!tls) { - if (uid < 0) - return hleLogError(Log::sceKernel, 0, "tlspl not found"); + if (tls || uid < 0) + return tls; - // There's this weird behavior where it looks up by index. Maybe we shouldn't use uids... - if (!tlsplUsedIndexes[(uid >> 3) & 15]) - return hleLogError(Log::sceKernel, 0, "tlspl not found"); + // There's this weird behavior where it looks up by index. Maybe we shouldn't use uids... + if (!tlsplUsedIndexes[(uid >> 3) & 15]) + return nullptr; - FindTLSByIndexArg state; - state.index = (uid >> 3) & 15; - kernelObjects.Iterate([&state](int id, TLSPL *possible) { - if (possible->ntls.index == state.index) { - state.result = possible; - return false; - } - return true; - }); - - if (!state.result) - return hleLogError(Log::sceKernel, 0, "tlspl not found"); - - tls = state.result; - } + FindTLSByIndexArg state; + state.index = (uid >> 3) & 15; + kernelObjects.Iterate([&state](int id, TLSPL *possible) { + if (possible->ntls.index == state.index) { + state.result = possible; + return false; + } + return true; + }); + return state.result; +} +// Returns the current thread's block in the pool, allocating it if needed, or 0 if the pool is full. +static u32 __KernelAllocateTls(TLSPL *tls) { SceUID threadID = __KernelGetCurThread(); int allocBlock = -1; bool needsClear = false; @@ -2062,18 +2070,14 @@ int sceKernelGetTlsAddr(SceUID uid) { if (allocBlock != -1) { tls->usage[allocBlock] = threadID; - tlsplThreadEndChecks.emplace(threadID, uid); + tlsplThreadEndChecks.emplace(threadID, tls->GetUID()); --tls->ntls.freeBlocks; needsClear = true; } } if (allocBlock == -1) - { - tls->waitingThreads.push_back(threadID); - __KernelWaitCurThread(WAITTYPE_TLSPL, uid, 1, 0, false, "allocate tls"); - return hleLogDebug(Log::sceKernel, 0, "waiting for tls alloc"); - } + return 0; u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1); u32 allocAddress = tls->address + allocBlock * alignedSize; @@ -2083,10 +2087,55 @@ int sceKernelGetTlsAddr(SceUID uid) { if (needsClear) { Memory::Memset(allocAddress, 0, tls->ntls.blockSize, "TlsAddr"); } + return allocAddress; +} +int sceKernelGetTlsAddr(SceUID uid) { + if (!__KernelIsDispatchEnabled() || __IsInInterrupt()) + return hleLogWarning(Log::sceKernel, 0, "dispatch disabled"); + + TLSPL *tls = __KernelFindTlspl(uid); + if (!tls) + return hleLogError(Log::sceKernel, 0, "tlspl not found"); + + u32 allocAddress = __KernelAllocateTls(tls); + if (allocAddress == 0) { + SceUID threadID = __KernelGetCurThread(); + tls->waitingThreads.push_back(threadID); + __KernelWaitCurThread(WAITTYPE_TLSPL, tls->GetUID(), TLSPL_WAITVALUE_RETURN_ADDR, 0, false, "allocate tls"); + return hleLogDebug(Log::sceKernel, 0, "waiting for tls alloc"); + } return hleLogDebug(Log::sceKernel, allocAddress); } +// The syscall behind usersystemlib's sceKernelGetTlsAddr, which calls it as (uid, &addr, 0) when +// the thread's cached address is null. Homebrew that has to run before usersystemlib.prx is loaded +// (like plugins) inlines that code, so it imports this directly. The third argument is unknown. +// We don't fill in the per-thread cache at $k0+0x40, so callers always take this path. +int _sceKernelAllocateTlspl(SceUID uid, u32 addrPtr, u32 unknown) { + if (__IsInInterrupt()) + return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "in interrupt"); + if (!__KernelIsDispatchEnabled()) + return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled"); + if (!Memory::IsValidRange(addrPtr, 4)) + return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "bad addr pointer"); + + TLSPL *tls = __KernelFindTlspl(uid); + if (!tls) + return hleLogError(Log::sceKernel, SCE_KERNEL_ERROR_UNKNOWN_TLSPL_ID, "tlspl not found"); + + u32 allocAddress = __KernelAllocateTls(tls); + if (allocAddress == 0) { + SceUID threadID = __KernelGetCurThread(); + tls->waitingThreads.push_back(threadID); + __KernelWaitCurThread(WAITTYPE_TLSPL, tls->GetUID(), addrPtr, 0, false, "allocate tls"); + return hleLogDebug(Log::sceKernel, 0, "waiting for tls alloc"); + } + + Memory::WriteOrException_U32(allocAddress, addrPtr); + return hleLogDebug(Log::sceKernel, 0, "addr=%08x", allocAddress); +} + // Parameters are an educated guess. int sceKernelFreeTlspl(SceUID uid) { diff --git a/Core/HLE/sceKernelMemory.h b/Core/HLE/sceKernelMemory.h index a65db34604..f35e137c7a 100644 --- a/Core/HLE/sceKernelMemory.h +++ b/Core/HLE/sceKernelMemory.h @@ -221,6 +221,7 @@ int sceKernelGetCompiledSdkVersion(); SceUID sceKernelCreateTlspl(const char *name, u32 partitionid, u32 attr, u32 size, u32 count, u32 optionsPtr); int sceKernelDeleteTlspl(SceUID uid); int sceKernelGetTlsAddr(SceUID uid); +int _sceKernelAllocateTlspl(SceUID uid, u32 addrPtr, u32 unknown); int sceKernelFreeTlspl(SceUID uid); int sceKernelReferTlsplStatus(SceUID uid, u32 infoPtr);