mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
Implement _sceKernelAllocateTlspl
This is the syscall usersystemlib's sceKernelGetTlsAddr makes when the thread's cached TLS address is null, as (uid, &addr, 0). Code that has to run before usersystemlib.prx is loaded (like plugins built with a Rust SDK) inlines sceKernelGetTlsAddr and imports this directly. Shares the allocation with sceKernelGetTlsAddr. A thread waiting on a full pool stores its address pointer as the wait value, so no state changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
47730e0eb0
commit
ac39c55c36
3 files changed
+87
-36
No files matched your search
@@ -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<TLSPL>(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<TLSPL>([&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<TLSPL>([&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)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user