Skip to content

Commit

Permalink
Merge pull request #16070 from unknownbrackets/tls-minor
Browse files Browse the repository at this point in the history
Kernel: Match index lookup behavior for tls
  • Loading branch information
hrydgard authored Sep 21, 2022
2 parents 3ff400e + 79c5c93 commit 1971fc4
Showing 1 changed file with 66 additions and 45 deletions.
111 changes: 66 additions & 45 deletions Core/HLE/sceKernelMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2174,68 +2174,89 @@ int sceKernelDeleteTlspl(SceUID uid)
return error;
}

int sceKernelGetTlsAddr(SceUID uid)
{
// TODO: Allocate downward if PSP_TLSPL_ATTR_HIGHMEM?
DEBUG_LOG(SCEKERNEL, "sceKernelGetTlsAddr(%08x)", uid);
struct FindTLSByIndexArg {
int index;
TLSPL *result = nullptr;
};

static bool FindTLSByIndex(TLSPL *possible, FindTLSByIndexArg *state) {
if (possible->ntls.index == state->index) {
state->result = possible;
return false;
}
return true;
}

int sceKernelGetTlsAddr(SceUID uid) {
if (!__KernelIsDispatchEnabled() || __IsInInterrupt())
return 0;
return hleLogWarning(SCEKERNEL, 0, "dispatch disabled");

u32 error;
TLSPL *tls = kernelObjects.Get<TLSPL>(uid, error);
if (tls)
if (!tls) {
if (uid < 0)
return hleLogError(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 hleLogError(SCEKERNEL, 0, "tlspl not found");

FindTLSByIndexArg state;
state.index = (uid >> 3) & 15;
kernelObjects.Iterate<TLSPL>(&FindTLSByIndex, &state);
if (!state.result)
return hleLogError(SCEKERNEL, 0, "tlspl not found");

tls = state.result;
}

SceUID threadID = __KernelGetCurThread();
int allocBlock = -1;
bool needsClear = false;

// If the thread already has one, return it.
for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i)
{
SceUID threadID = __KernelGetCurThread();
int allocBlock = -1;
bool needsClear = false;
if (tls->usage[i] == threadID)
allocBlock = (int) i;
}

// If the thread already has one, return it.
if (allocBlock == -1)
{
for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i)
{
if (tls->usage[i] == threadID)
allocBlock = (int) i;
}

if (allocBlock == -1)
{
for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i)
{
// The PSP doesn't give the same block out twice in a row, even if freed.
if (tls->usage[tls->next] == 0)
allocBlock = tls->next;
tls->next = (tls->next + 1) % tls->ntls.totalBlocks;
}

if (allocBlock != -1)
{
tls->usage[allocBlock] = threadID;
tlsplThreadEndChecks.insert(std::make_pair(threadID, uid));
--tls->ntls.freeBlocks;
needsClear = true;
}
// The PSP doesn't give the same block out twice in a row, even if freed.
if (tls->usage[tls->next] == 0)
allocBlock = tls->next;
tls->next = (tls->next + 1) % tls->ntls.totalBlocks;
}

if (allocBlock == -1)
if (allocBlock != -1)
{
tls->waitingThreads.push_back(threadID);
__KernelWaitCurThread(WAITTYPE_TLSPL, uid, 1, 0, false, "allocate tls");
return 0;
tls->usage[allocBlock] = threadID;
tlsplThreadEndChecks.insert(std::make_pair(threadID, uid));
--tls->ntls.freeBlocks;
needsClear = true;
}
}

u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1);
u32 allocAddress = tls->address + allocBlock * alignedSize;
NotifyMemInfo(MemBlockFlags::SUB_ALLOC, allocAddress, tls->ntls.blockSize, "TlsAddr");
if (allocBlock == -1)
{
tls->waitingThreads.push_back(threadID);
__KernelWaitCurThread(WAITTYPE_TLSPL, uid, 1, 0, false, "allocate tls");
return hleLogDebug(SCEKERNEL, 0, "waiting for tls alloc");
}

// We clear the blocks upon first allocation (and also when they are freed, both are necessary.)
if (needsClear) {
Memory::Memset(allocAddress, 0, tls->ntls.blockSize, "TlsAddr");
}
u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1);
u32 allocAddress = tls->address + allocBlock * alignedSize;
NotifyMemInfo(MemBlockFlags::SUB_ALLOC, allocAddress, tls->ntls.blockSize, "TlsAddr");

return allocAddress;
// We clear the blocks upon first allocation (and also when they are freed, both are necessary.)
if (needsClear) {
Memory::Memset(allocAddress, 0, tls->ntls.blockSize, "TlsAddr");
}
else
return 0;

return hleLogDebug(SCEKERNEL, allocAddress);
}

// Parameters are an educated guess.
Expand Down

0 comments on commit 1971fc4

Please sign in to comment.