1
// Copyright (c) 2012- PPSSPP Project.
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
23
#include "base/compat.h"
25
#include "Common/ChunkFile.h"
26
#include "Core/HLE/HLE.h"
27
#include "Core/HLE/FunctionWrappers.h"
28
#include "Core/System.h"
29
#include "Core/MIPS/MIPS.h"
30
#include "Core/MemMapHelpers.h"
31
#include "Core/CoreTiming.h"
32
#include "Core/Reporting.h"
34
#include "Core/HLE/sceKernel.h"
35
#include "Core/HLE/sceKernelThread.h"
36
#include "Core/HLE/sceKernelInterrupt.h"
37
#include "Core/HLE/sceKernelMemory.h"
38
#include "Core/HLE/KernelWaitHelpers.h"
40
const int TLSPL_NUM_INDEXES = 16;
42
//////////////////////////////////////////////////////////////////////////
44
BlockAllocator userMemory(256);
45
BlockAllocator kernelMemory(256);
47
static int vplWaitTimer = -1;
48
static int fplWaitTimer = -1;
49
static bool tlsplUsedIndexes[TLSPL_NUM_INDEXES];
51
// Thread -> TLSPL uids for thread end.
52
typedef std::multimap<SceUID, SceUID> TlsplMap;
53
static TlsplMap tlsplThreadEndChecks;
55
//////////////////////////////////////////////////////////////////////////
57
#define SCE_KERNEL_HASCOMPILEDSDKVERSION 0x1000
58
#define SCE_KERNEL_HASCOMPILERVERSION 0x2000
64
struct FplWaitingThread
70
bool operator ==(const SceUID &otherThreadID) const
72
return threadID == otherThreadID;
79
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
85
s32_le numWaitThreads;
88
//FPL - Fixed Length Dynamic Memory Pool - every item has the same length
89
struct FPL : public KernelObject
91
FPL() : blocks(NULL), nextBlock(0) {}
97
const char *GetName() override { return nf.name; }
98
const char *GetTypeName() override { return "FPL"; }
99
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_FPLID; }
100
static int GetStaticIDType() { return SCE_KERNEL_TMID_Fpl; }
101
int GetIDType() const override { return SCE_KERNEL_TMID_Fpl; }
103
int findFreeBlock() {
104
for (int i = 0; i < nf.numBlocks; i++) {
105
int b = nextBlock++ % nf.numBlocks;
113
int allocateBlock() {
114
int block = findFreeBlock();
116
blocks[block] = true;
120
bool freeBlock(int b) {
128
void DoState(PointerWrap &p) override
130
auto s = p.Section("FPL", 1);
135
if (p.mode == p.MODE_READ)
136
blocks = new bool[nf.numBlocks];
137
p.DoArray(blocks, nf.numBlocks);
141
FplWaitingThread dv = {0};
142
p.Do(waitingThreads, dv);
151
std::vector<FplWaitingThread> waitingThreads;
152
// Key is the callback id it was for, or if no callback, the thread id.
153
std::map<SceUID, FplWaitingThread> pausedWaits;
156
struct VplWaitingThread
162
bool operator ==(const SceUID &otherThreadID) const
164
return threadID == otherThreadID;
168
struct SceKernelVplInfo
171
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
175
s32_le numWaitThreads;
178
struct SceKernelVplBlock
180
PSPPointer<SceKernelVplBlock> next;
181
// Includes this info (which is 1 block / 8 bytes.)
185
struct SceKernelVplHeader {
187
// TODO: Why twice? Is there a case it changes?
191
u32_le allocatedInBlocks_;
192
PSPPointer<SceKernelVplBlock> nextFreeBlock_;
193
SceKernelVplBlock firstBlock_;
195
void Init(u32 ptr, u32 size) {
199
sizeMinus8_ = size - 8;
200
allocatedInBlocks_ = 0;
201
nextFreeBlock_ = FirstBlockPtr();
203
firstBlock_.next = LastBlockPtr();
204
// Includes its own header, which is one block.
205
firstBlock_.sizeInBlocks = (size - 0x28) / 8 + 1;
207
auto lastBlock = LastBlock();
208
lastBlock->next = FirstBlockPtr();
209
lastBlock->sizeInBlocks = 0;
212
u32 Allocate(u32 size) {
213
u32 allocBlocks = ((size + 7) / 8) + 1;
214
auto prev = nextFreeBlock_;
217
if (b->sizeInBlocks > allocBlocks) {
218
if (nextFreeBlock_ == b) {
219
nextFreeBlock_ = prev;
222
b = SplitBlock(b, allocBlocks);
225
if (b->sizeInBlocks == allocBlocks) {
226
UnlinkFreeBlock(b, prev);
231
} while (prev.IsValid() && prev != nextFreeBlock_);
237
auto b = PSPPointer<SceKernelVplBlock>::Create(ptr - 8);
238
// Is it even in the right range? Can't be the last block, which is always 0.
239
if (!b.IsValid() || ptr < FirstBlockPtr() || ptr >= LastBlockPtr()) {
242
// Great, let's check if it matches our magic.
243
if (b->next.ptr != SentinelPtr() || b->sizeInBlocks > allocatedInBlocks_) {
247
auto prev = LastBlock();
249
auto next = prev->next;
253
} else if (next > b) {
254
LinkFreeBlock(b, prev, next);
259
} while (prev.IsValid() && prev != LastBlock());
265
u32 FreeSize() const {
266
// Size less the header and number of allocated bytes.
267
return sizeMinus8_ + 8 - 0x20 - allocatedInBlocks_ * 8;
270
bool LinkFreeBlock(PSPPointer<SceKernelVplBlock> b, PSPPointer<SceKernelVplBlock> prev, PSPPointer<SceKernelVplBlock> next) {
271
allocatedInBlocks_ -= b->sizeInBlocks;
272
nextFreeBlock_ = prev;
274
// Make sure we don't consider it free later by erasing the magic.
276
const auto afterB = b + b->sizeInBlocks;
277
if (afterB == next && next->sizeInBlocks != 0) {
278
b = MergeBlocks(b, next);
281
const auto afterPrev = prev + prev->sizeInBlocks;
282
if (afterPrev == b) {
283
b = MergeBlocks(prev, b);
291
void UnlinkFreeBlock(PSPPointer<SceKernelVplBlock> b, PSPPointer<SceKernelVplBlock> prev) {
292
allocatedInBlocks_ += b->sizeInBlocks;
293
prev->next = b->next;
294
if (nextFreeBlock_ == b) {
295
nextFreeBlock_ = prev;
297
b->next = SentinelPtr();
300
PSPPointer<SceKernelVplBlock> SplitBlock(PSPPointer<SceKernelVplBlock> b, u32 allocBlocks) {
301
u32 prev = b->next.ptr;
302
b->sizeInBlocks -= allocBlocks;
303
b->next = b + b->sizeInBlocks;
305
b += b->sizeInBlocks;
306
b->sizeInBlocks = allocBlocks;
312
inline void Validate() {
313
auto lastBlock = LastBlock();
314
_dbg_assert_msg_(SCEKERNEL, nextFreeBlock_->next.ptr != SentinelPtr(), "Next free block should not be allocated.");
315
_dbg_assert_msg_(SCEKERNEL, nextFreeBlock_->next.ptr != sentinel_, "Next free block should not point to sentinel.");
316
_dbg_assert_msg_(SCEKERNEL, lastBlock->sizeInBlocks == 0, "Last block should have size of 0.");
317
_dbg_assert_msg_(SCEKERNEL, lastBlock->next.ptr != SentinelPtr(), "Last block should not be allocated.");
318
_dbg_assert_msg_(SCEKERNEL, lastBlock->next.ptr != sentinel_, "Last block should not point to sentinel.");
320
auto b = PSPPointer<SceKernelVplBlock>::Create(FirstBlockPtr());
321
bool sawFirstFree = false;
322
while (b.ptr < lastBlock.ptr) {
323
bool isFree = b->next.ptr != SentinelPtr();
326
_dbg_assert_msg_(SCEKERNEL, lastBlock->next.ptr == b.ptr, "Last block should point to first free block.");
329
_dbg_assert_msg_(SCEKERNEL, b->next.ptr != SentinelPtr(), "Free blocks should only point to other free blocks.");
330
_dbg_assert_msg_(SCEKERNEL, b->next.ptr > b.ptr, "Free blocks should be in order.");
331
_dbg_assert_msg_(SCEKERNEL, b + b->sizeInBlocks < b->next || b->next.ptr == lastBlock.ptr, "Two free blocks should not be next to each other.");
333
_dbg_assert_msg_(SCEKERNEL, b->next.ptr == SentinelPtr(), "Allocated blocks should point to the sentinel.");
335
_dbg_assert_msg_(SCEKERNEL, b->sizeInBlocks != 0, "Only the last block should have a size of 0.");
336
b += b->sizeInBlocks;
339
_dbg_assert_msg_(SCEKERNEL, lastBlock->next.ptr == lastBlock.ptr, "Last block should point to itself when full.");
341
_dbg_assert_msg_(SCEKERNEL, b.ptr == lastBlock.ptr, "Blocks should not extend outside vpl.");
345
auto b = PSPPointer<SceKernelVplBlock>::Create(FirstBlockPtr());
346
auto lastBlock = LastBlock();
347
while (b.ptr < lastBlock.ptr) {
348
bool isFree = b->next.ptr != SentinelPtr();
349
if (nextFreeBlock_ == b && isFree) {
350
NOTICE_LOG(HLE, "NEXT: %x -> %x (size %x)", b.ptr - startPtr_, b->next.ptr - startPtr_, b->sizeInBlocks * 8);
352
NOTICE_LOG(HLE, "FREE: %x -> %x (size %x)", b.ptr - startPtr_, b->next.ptr - startPtr_, b->sizeInBlocks * 8);
354
NOTICE_LOG(HLE, "BLOCK: %x (size %x)", b.ptr - startPtr_, b->sizeInBlocks * 8);
356
b += b->sizeInBlocks;
358
NOTICE_LOG(HLE, "LAST: %x -> %x (size %x)", lastBlock.ptr - startPtr_, lastBlock->next.ptr - startPtr_, lastBlock->sizeInBlocks * 8);
361
PSPPointer<SceKernelVplBlock> MergeBlocks(PSPPointer<SceKernelVplBlock> first, PSPPointer<SceKernelVplBlock> second) {
362
first->sizeInBlocks += second->sizeInBlocks;
363
first->next = second->next;
367
u32 FirstBlockPtr() const {
368
return startPtr_ + 0x18;
371
u32 LastBlockPtr() const {
372
return startPtr_ + sizeMinus8_;
375
PSPPointer<SceKernelVplBlock> LastBlock() {
376
return PSPPointer<SceKernelVplBlock>::Create(LastBlockPtr());
379
u32 SentinelPtr() const {
380
return startPtr_ + 8;
384
struct VPL : public KernelObject
386
const char *GetName() override { return nv.name; }
387
const char *GetTypeName() override { return "VPL"; }
388
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_VPLID; }
389
static int GetStaticIDType() { return SCE_KERNEL_TMID_Vpl; }
390
int GetIDType() const override { return SCE_KERNEL_TMID_Vpl; }
396
void DoState(PointerWrap &p) override {
397
auto s = p.Section("VPL", 1, 2);
404
VplWaitingThread dv = {0};
405
p.Do(waitingThreads, dv);
416
std::vector<VplWaitingThread> waitingThreads;
417
// Key is the callback id it was for, or if no callback, the thread id.
418
std::map<SceUID, VplWaitingThread> pausedWaits;
419
BlockAllocator alloc;
420
PSPPointer<SceKernelVplHeader> header;
423
void __KernelVplTimeout(u64 userdata, int cyclesLate);
424
void __KernelFplTimeout(u64 userdata, int cyclesLate);
425
void __KernelTlsplThreadEnd(SceUID threadID);
427
void __KernelVplBeginCallback(SceUID threadID, SceUID prevCallbackId);
428
void __KernelVplEndCallback(SceUID threadID, SceUID prevCallbackId);
429
void __KernelFplBeginCallback(SceUID threadID, SceUID prevCallbackId);
430
void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId);
432
void __KernelMemoryInit()
434
kernelMemory.Init(PSP_GetKernelMemoryBase(), PSP_GetKernelMemoryEnd()-PSP_GetKernelMemoryBase());
435
userMemory.Init(PSP_GetUserMemoryBase(), PSP_GetUserMemoryEnd()-PSP_GetUserMemoryBase());
436
INFO_LOG(SCEKERNEL, "Kernel and user memory pools initialized");
438
vplWaitTimer = CoreTiming::RegisterEvent("VplTimeout", __KernelVplTimeout);
439
fplWaitTimer = CoreTiming::RegisterEvent("FplTimeout", __KernelFplTimeout);
443
compilerVersion_ = 0;
444
memset(tlsplUsedIndexes, 0, sizeof(tlsplUsedIndexes));
446
__KernelListenThreadEnd(&__KernelTlsplThreadEnd);
448
__KernelRegisterWaitTypeFuncs(WAITTYPE_VPL, __KernelVplBeginCallback, __KernelVplEndCallback);
449
__KernelRegisterWaitTypeFuncs(WAITTYPE_FPL, __KernelFplBeginCallback, __KernelFplEndCallback);
451
// The kernel statically allocates this memory, which has some code in it.
452
// It appears this is used for some common funcs in Kernel_Library (memcpy, lwmutex, suspend intr, etc.)
453
// Allocating this block is necessary to have the same memory semantics as real firmware.
454
userMemory.AllocAt(PSP_GetUserMemoryBase(), 0x4000, "usersystemlib");
457
void __KernelMemoryDoState(PointerWrap &p)
459
auto s = p.Section("sceKernelMemory", 1, 2);
463
kernelMemory.DoState(p);
464
userMemory.DoState(p);
467
CoreTiming::RestoreRegisterEvent(vplWaitTimer, "VplTimeout", __KernelVplTimeout);
469
CoreTiming::RestoreRegisterEvent(fplWaitTimer, "FplTimeout", __KernelFplTimeout);
472
p.Do(compilerVersion_);
473
p.DoArray(tlsplUsedIndexes, ARRAY_SIZE(tlsplUsedIndexes));
475
p.Do(tlsplThreadEndChecks);
479
void __KernelMemoryShutdown()
482
INFO_LOG(SCEKERNEL,"Shutting down user memory pool: ");
483
userMemory.ListBlocks();
485
userMemory.Shutdown();
487
INFO_LOG(SCEKERNEL,"Shutting down \"kernel\" memory pool: ");
488
kernelMemory.ListBlocks();
490
kernelMemory.Shutdown();
491
tlsplThreadEndChecks.clear();
494
enum SceKernelFplAttr
496
PSP_FPL_ATTR_FIFO = 0x0000,
497
PSP_FPL_ATTR_PRIORITY = 0x0100,
498
PSP_FPL_ATTR_HIGHMEM = 0x4000,
499
PSP_FPL_ATTR_KNOWN = PSP_FPL_ATTR_FIFO | PSP_FPL_ATTR_PRIORITY | PSP_FPL_ATTR_HIGHMEM,
502
static bool __KernelUnlockFplForThread(FPL *fpl, FplWaitingThread &threadInfo, u32 &error, int result, bool &wokeThreads)
504
const SceUID threadID = threadInfo.threadID;
505
if (!HLEKernel::VerifyWait(threadID, WAITTYPE_FPL, fpl->GetUID()))
508
// If result is an error code, we're just letting it go.
511
int blockNum = fpl->allocateBlock();
514
u32 blockPtr = fpl->address + fpl->alignedSize * blockNum;
515
Memory::Write_U32(blockPtr, threadInfo.addrPtr);
521
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
522
if (timeoutPtr != 0 && fplWaitTimer != -1)
524
// Remove any event for this thread.
525
s64 cyclesLeft = CoreTiming::UnscheduleEvent(fplWaitTimer, threadID);
526
Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
529
__KernelResumeThreadFromWait(threadID, result);
534
void __KernelFplBeginCallback(SceUID threadID, SceUID prevCallbackId)
536
auto result = HLEKernel::WaitBeginCallback<FPL, WAITTYPE_FPL, FplWaitingThread>(threadID, prevCallbackId, fplWaitTimer);
537
if (result == HLEKernel::WAIT_CB_SUCCESS)
538
DEBUG_LOG(SCEKERNEL, "sceKernelAllocateFplCB: Suspending fpl wait for callback");
539
else if (result == HLEKernel::WAIT_CB_BAD_WAIT_DATA)
540
ERROR_LOG_REPORT(SCEKERNEL, "sceKernelAllocateFplCB: wait not found to pause for callback");
542
WARN_LOG_REPORT(SCEKERNEL, "sceKernelAllocateFplCB: beginning callback with bad wait id?");
545
void __KernelFplEndCallback(SceUID threadID, SceUID prevCallbackId)
547
auto result = HLEKernel::WaitEndCallback<FPL, WAITTYPE_FPL, FplWaitingThread>(threadID, prevCallbackId, fplWaitTimer, __KernelUnlockFplForThread);
548
if (result == HLEKernel::WAIT_CB_RESUMED_WAIT)
549
DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB: Resuming mbx wait from callback");
552
static bool __FplThreadSortPriority(FplWaitingThread thread1, FplWaitingThread thread2)
554
return __KernelThreadSortPriority(thread1.threadID, thread2.threadID);
557
static bool __KernelClearFplThreads(FPL *fpl, int reason)
560
bool wokeThreads = false;
561
for (auto iter = fpl->waitingThreads.begin(), end = fpl->waitingThreads.end(); iter != end; ++iter)
562
__KernelUnlockFplForThread(fpl, *iter, error, reason, wokeThreads);
563
fpl->waitingThreads.clear();
568
static void __KernelSortFplThreads(FPL *fpl)
570
// Remove any that are no longer waiting.
571
SceUID uid = fpl->GetUID();
572
HLEKernel::CleanupWaitingThreads(WAITTYPE_FPL, uid, fpl->waitingThreads);
574
if ((fpl->nf.attr & PSP_FPL_ATTR_PRIORITY) != 0)
575
std::stable_sort(fpl->waitingThreads.begin(), fpl->waitingThreads.end(), __FplThreadSortPriority);
578
int sceKernelCreateFpl(const char *name, u32 mpid, u32 attr, u32 blockSize, u32 numBlocks, u32 optPtr)
582
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid name", SCE_KERNEL_ERROR_NO_MEMORY);
583
return SCE_KERNEL_ERROR_NO_MEMORY;
585
if (mpid < 1 || mpid > 9 || mpid == 7)
587
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, mpid);
588
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
590
// We only support user right now.
591
if (mpid != 2 && mpid != 6)
593
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_PERM, mpid);
594
return SCE_KERNEL_ERROR_ILLEGAL_PERM;
596
if (((attr & ~PSP_FPL_ATTR_KNOWN) & ~0xFF) != 0)
598
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
599
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
601
// There's probably a simpler way to get this same basic formula...
602
// This is based on results from a PSP.
603
bool illegalMemSize = blockSize == 0 || numBlocks == 0;
604
if (!illegalMemSize && (u64) blockSize > ((0x100000000ULL / (u64) numBlocks) - 4ULL))
605
illegalMemSize = true;
606
if (!illegalMemSize && (u64) numBlocks >= 0x100000000ULL / (((u64) blockSize + 3ULL) & ~3ULL))
607
illegalMemSize = true;
610
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid blockSize/count", SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE);
611
return SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE;
617
u32 size = Memory::Read_U32(optPtr);
619
WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateFpl(): unsupported extra options, size = %d", size);
621
alignment = Memory::Read_U32(optPtr + 4);
622
// Must be a power of 2 to be valid.
623
if ((alignment & (alignment - 1)) != 0)
625
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateFpl(): invalid alignment %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, alignment);
626
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
633
int alignedSize = ((int)blockSize + alignment - 1) & ~(alignment - 1);
634
u32 totalSize = alignedSize * numBlocks;
635
bool atEnd = (attr & PSP_FPL_ATTR_HIGHMEM) != 0;
636
u32 address = userMemory.Alloc(totalSize, atEnd, "FPL");
637
if (address == (u32)-1)
639
DEBUG_LOG(SCEKERNEL, "sceKernelCreateFpl(\"%s\", partition=%i, attr=%08x, bsize=%i, nb=%i) FAILED - out of ram",
640
name, mpid, attr, blockSize, numBlocks);
641
return SCE_KERNEL_ERROR_NO_MEMORY;
645
SceUID id = kernelObjects.Create(fpl);
647
strncpy(fpl->nf.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
648
fpl->nf.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
650
fpl->nf.size = sizeof(fpl->nf);
651
fpl->nf.blocksize = blockSize;
652
fpl->nf.numBlocks = numBlocks;
653
fpl->nf.numFreeBlocks = numBlocks;
654
fpl->nf.numWaitThreads = 0;
656
fpl->blocks = new bool[fpl->nf.numBlocks];
657
memset(fpl->blocks, 0, fpl->nf.numBlocks * sizeof(bool));
658
fpl->address = address;
659
fpl->alignedSize = alignedSize;
661
DEBUG_LOG(SCEKERNEL, "%i=sceKernelCreateFpl(\"%s\", partition=%i, attr=%08x, bsize=%i, nb=%i)",
662
id, name, mpid, attr, blockSize, numBlocks);
667
int sceKernelDeleteFpl(SceUID uid)
671
FPL *fpl = kernelObjects.Get<FPL>(uid, error);
674
DEBUG_LOG(SCEKERNEL, "sceKernelDeleteFpl(%i)", uid);
676
bool wokeThreads = __KernelClearFplThreads(fpl, SCE_KERNEL_ERROR_WAIT_DELETE);
678
hleReSchedule("fpl deleted");
680
userMemory.Free(fpl->address);
681
return kernelObjects.Destroy<FPL>(uid);
685
DEBUG_LOG(SCEKERNEL, "sceKernelDeleteFpl(%i): invalid fpl", uid);
690
void __KernelFplTimeout(u64 userdata, int cyclesLate)
692
SceUID threadID = (SceUID) userdata;
693
HLEKernel::WaitExecTimeout<FPL, WAITTYPE_FPL>(threadID);
696
static void __KernelSetFplTimeout(u32 timeoutPtr)
698
if (timeoutPtr == 0 || fplWaitTimer == -1)
701
int micro = (int) Memory::Read_U32(timeoutPtr);
703
// TODO: test for fpls.
704
// This happens to be how the hardware seems to time things.
707
// Yes, this 7 is reproducible. 6 is (a lot) longer than 7.
710
else if (micro <= 215)
713
CoreTiming::ScheduleEvent(usToCycles(micro), fplWaitTimer, __KernelGetCurThread());
716
int sceKernelAllocateFpl(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr)
719
FPL *fpl = kernelObjects.Get<FPL>(uid, error);
722
DEBUG_LOG(SCEKERNEL, "sceKernelAllocateFpl(%i, %08x, %08x)", uid, blockPtrAddr, timeoutPtr);
724
int blockNum = fpl->allocateBlock();
726
u32 blockPtr = fpl->address + fpl->alignedSize * blockNum;
727
Memory::Write_U32(blockPtr, blockPtrAddr);
729
SceUID threadID = __KernelGetCurThread();
730
HLEKernel::RemoveWaitingThread(fpl->waitingThreads, threadID);
731
FplWaitingThread waiting = {threadID, blockPtrAddr};
732
fpl->waitingThreads.push_back(waiting);
734
__KernelSetFplTimeout(timeoutPtr);
735
__KernelWaitCurThread(WAITTYPE_FPL, uid, 0, timeoutPtr, false, "fpl waited");
742
DEBUG_LOG(SCEKERNEL, "sceKernelAllocateFpl(%i, %08x, %08x): invalid fpl", uid, blockPtrAddr, timeoutPtr);
747
int sceKernelAllocateFplCB(SceUID uid, u32 blockPtrAddr, u32 timeoutPtr)
750
FPL *fpl = kernelObjects.Get<FPL>(uid, error);
753
DEBUG_LOG(SCEKERNEL, "sceKernelAllocateFplCB(%i, %08x, %08x)", uid, blockPtrAddr, timeoutPtr);
755
int blockNum = fpl->allocateBlock();
757
u32 blockPtr = fpl->address + fpl->alignedSize * blockNum;
758
Memory::Write_U32(blockPtr, blockPtrAddr);
760
SceUID threadID = __KernelGetCurThread();
761
HLEKernel::RemoveWaitingThread(fpl->waitingThreads, threadID);
762
FplWaitingThread waiting = {threadID, blockPtrAddr};
763
fpl->waitingThreads.push_back(waiting);
765
__KernelSetFplTimeout(timeoutPtr);
766
__KernelWaitCurThread(WAITTYPE_FPL, uid, 0, timeoutPtr, true, "fpl waited");
773
DEBUG_LOG(SCEKERNEL, "sceKernelAllocateFplCB(%i, %08x, %08x): invalid fpl", uid, blockPtrAddr, timeoutPtr);
778
int sceKernelTryAllocateFpl(SceUID uid, u32 blockPtrAddr)
781
FPL *fpl = kernelObjects.Get<FPL>(uid, error);
784
DEBUG_LOG(SCEKERNEL, "sceKernelTryAllocateFpl(%i, %08x)", uid, blockPtrAddr);
786
int blockNum = fpl->allocateBlock();
788
u32 blockPtr = fpl->address + fpl->alignedSize * blockNum;
789
Memory::Write_U32(blockPtr, blockPtrAddr);
792
return SCE_KERNEL_ERROR_NO_MEMORY;
797
DEBUG_LOG(SCEKERNEL, "sceKernelTryAllocateFpl(%i, %08x): invalid fpl", uid, blockPtrAddr);
802
int sceKernelFreeFpl(SceUID uid, u32 blockPtr)
804
if (blockPtr > PSP_GetUserMemoryEnd()) {
805
WARN_LOG(SCEKERNEL, "%08x=sceKernelFreeFpl(%i, %08x): invalid address", SCE_KERNEL_ERROR_ILLEGAL_ADDR, uid, blockPtr);
806
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
810
FPL *fpl = kernelObjects.Get<FPL>(uid, error);
812
int blockNum = (blockPtr - fpl->address) / fpl->alignedSize;
813
if (blockNum < 0 || blockNum >= fpl->nf.numBlocks) {
814
DEBUG_LOG(SCEKERNEL, "sceKernelFreeFpl(%i, %08x): bad block ptr", uid, blockPtr);
815
return SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCK;
817
if (fpl->freeBlock(blockNum)) {
818
DEBUG_LOG(SCEKERNEL, "sceKernelFreeFpl(%i, %08x)", uid, blockPtr);
819
__KernelSortFplThreads(fpl);
821
bool wokeThreads = false;
823
for (auto iter = fpl->waitingThreads.begin(), end = fpl->waitingThreads.end(); iter != end; ++iter)
825
if (__KernelUnlockFplForThread(fpl, *iter, error, 0, wokeThreads))
827
fpl->waitingThreads.erase(iter);
833
hleReSchedule("fpl freed");
836
DEBUG_LOG(SCEKERNEL, "sceKernelFreeFpl(%i, %08x): already free", uid, blockPtr);
837
return SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCK;
843
DEBUG_LOG(SCEKERNEL, "sceKernelFreeFpl(%i, %08x): invalid fpl", uid, blockPtr);
848
int sceKernelCancelFpl(SceUID uid, u32 numWaitThreadsPtr)
853
FPL *fpl = kernelObjects.Get<FPL>(uid, error);
856
DEBUG_LOG(SCEKERNEL, "sceKernelCancelFpl(%i, %08x)", uid, numWaitThreadsPtr);
857
fpl->nf.numWaitThreads = (int) fpl->waitingThreads.size();
858
if (Memory::IsValidAddress(numWaitThreadsPtr))
859
Memory::Write_U32(fpl->nf.numWaitThreads, numWaitThreadsPtr);
861
bool wokeThreads = __KernelClearFplThreads(fpl, SCE_KERNEL_ERROR_WAIT_CANCEL);
863
hleReSchedule("fpl canceled");
868
DEBUG_LOG(SCEKERNEL, "sceKernelCancelFpl(%i, %08x): invalid fpl", uid, numWaitThreadsPtr);
873
int sceKernelReferFplStatus(SceUID uid, u32 statusPtr)
876
FPL *fpl = kernelObjects.Get<FPL>(uid, error);
879
DEBUG_LOG(SCEKERNEL, "sceKernelReferFplStatus(%i, %08x)", uid, statusPtr);
880
// Refresh waiting threads and free block count.
881
__KernelSortFplThreads(fpl);
882
fpl->nf.numWaitThreads = (int) fpl->waitingThreads.size();
883
fpl->nf.numFreeBlocks = 0;
884
for (int i = 0; i < (int)fpl->nf.numBlocks; ++i)
887
++fpl->nf.numFreeBlocks;
889
if (Memory::Read_U32(statusPtr) != 0)
890
Memory::WriteStruct(statusPtr, &fpl->nf);
895
DEBUG_LOG(SCEKERNEL, "sceKernelReferFplStatus(%i, %08x): invalid fpl", uid, statusPtr);
902
//////////////////////////////////////////////////////////////////////////
904
//////////////////////////////////////////////////////////////////////////
905
//00:49:12 <TyRaNiD> ector, well the partitions are 1 = kernel, 2 = user, 3 = me, 4 = kernel mirror :)
907
class PartitionMemoryBlock : public KernelObject
910
const char *GetName() override { return name; }
911
const char *GetTypeName() override { return "MemoryPart"; }
912
void GetQuickInfo(char *ptr, int size) override
914
int sz = alloc->GetBlockSizeFromAddress(address);
915
snprintf(ptr, size, "MemPart: %08x - %08x size: %08x", address, address + sz, sz);
917
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_UID; }
918
static int GetStaticIDType() { return PPSSPP_KERNEL_TMID_PMB; }
919
int GetIDType() const override { return PPSSPP_KERNEL_TMID_PMB; }
921
PartitionMemoryBlock(BlockAllocator *_alloc, const char *_name, u32 size, MemblockType type, u32 alignment)
924
strncpy(name, _name, 32);
927
// 0 is used for save states to wake up.
930
if (type == PSP_SMEM_Addr)
933
address = alloc->AllocAt(alignment, size, name);
935
else if (type == PSP_SMEM_LowAligned || type == PSP_SMEM_HighAligned)
936
address = alloc->AllocAligned(size, 0x100, alignment, type == PSP_SMEM_HighAligned, name);
938
address = alloc->Alloc(size, type == PSP_SMEM_High, name);
944
~PartitionMemoryBlock()
946
if (address != (u32)-1)
947
alloc->Free(address);
949
bool IsValid() {return address != (u32)-1;}
950
BlockAllocator *alloc;
952
void DoState(PointerWrap &p) override
954
auto s = p.Section("PMB", 1);
959
p.DoArray(name, sizeof(name));
967
static u32 sceKernelMaxFreeMemSize()
969
u32 retVal = userMemory.GetLargestFreeBlockSize();
970
DEBUG_LOG(SCEKERNEL, "%08x (dec %i)=sceKernelMaxFreeMemSize()", retVal, retVal);
974
static u32 sceKernelTotalFreeMemSize()
976
u32 retVal = userMemory.GetTotalFreeBytes();
977
DEBUG_LOG(SCEKERNEL, "%08x (dec %i)=sceKernelTotalFreeMemSize()", retVal, retVal);
981
static int sceKernelAllocPartitionMemory(int partition, const char *name, int type, u32 size, u32 addr)
985
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid name", SCE_KERNEL_ERROR_ERROR);
986
return SCE_KERNEL_ERROR_ERROR;
990
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid size %x", SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED, size);
991
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
993
if (partition < 1 || partition > 9 || partition == 7)
995
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid partition %x", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, partition);
996
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
998
// We only support user right now.
999
if (partition != 2 && partition != 5 && partition != 6)
1001
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid partition %x", SCE_KERNEL_ERROR_ILLEGAL_PARTITION, partition);
1002
return SCE_KERNEL_ERROR_ILLEGAL_PARTITION;
1004
if (type < PSP_SMEM_Low || type > PSP_SMEM_HighAligned)
1006
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid type %x", SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE, type);
1007
return SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE;
1009
// Alignment is only allowed for powers of 2.
1010
if ((type == PSP_SMEM_LowAligned || type == PSP_SMEM_HighAligned) && ((addr & (addr - 1)) != 0 || addr == 0))
1012
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelAllocPartitionMemory(): invalid alignment %x", SCE_KERNEL_ERROR_ILLEGAL_ALIGNMENT_SIZE, addr);
1013
return SCE_KERNEL_ERROR_ILLEGAL_ALIGNMENT_SIZE;
1016
PartitionMemoryBlock *block = new PartitionMemoryBlock(&userMemory, name, size, (MemblockType)type, addr);
1017
if (!block->IsValid())
1020
ERROR_LOG(SCEKERNEL, "sceKernelAllocPartitionMemory(partition = %i, %s, type= %i, size= %i, addr= %08x): allocation failed", partition, name, type, size, addr);
1021
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
1023
SceUID uid = kernelObjects.Create(block);
1025
DEBUG_LOG(SCEKERNEL,"%i = sceKernelAllocPartitionMemory(partition = %i, %s, type= %i, size= %i, addr= %08x)",
1026
uid, partition, name, type, size, addr);
1031
static int sceKernelFreePartitionMemory(SceUID id)
1033
DEBUG_LOG(SCEKERNEL,"sceKernelFreePartitionMemory(%d)",id);
1035
return kernelObjects.Destroy<PartitionMemoryBlock>(id);
1038
static u32 sceKernelGetBlockHeadAddr(SceUID id)
1041
PartitionMemoryBlock *block = kernelObjects.Get<PartitionMemoryBlock>(id, error);
1044
DEBUG_LOG(SCEKERNEL,"%08x = sceKernelGetBlockHeadAddr(%i)", block->address, id);
1045
return block->address;
1049
ERROR_LOG(SCEKERNEL,"sceKernelGetBlockHeadAddr failed(%i)", id);
1055
static int sceKernelPrintf(const char *formatString)
1057
if (formatString == NULL)
1060
bool supported = true;
1063
char tempFormat[24] = {'%'};
1064
std::string result, format = formatString;
1066
// Each printf is a separate line already in the log, so don't double space.
1067
// This does mean we break up strings, unfortunately.
1068
if (!format.empty() && format[format.size() - 1] == '\n')
1069
format.resize(format.size() - 1);
1071
for (size_t i = 0, n = format.size(); supported && i < n; )
1073
size_t next = format.find('%', i);
1074
if (next == format.npos)
1076
result += format.substr(i);
1080
result += format.substr(i, next - i);
1098
s = Memory::GetCharPointer(PARAM(param++));
1099
result += s ? s : "(null)";
1108
tempFormat[1] = format[i];
1109
tempFormat[2] = '\0';
1110
snprintf(tempStr, sizeof(tempStr), tempFormat, PARAM(param++));
1116
if (i + 3 > n || format[i + 1] != '8' || (format[i + 2] != 'x' && format[i + 2] != 'X'))
1120
// These are the '0', '8', and 'x' or 'X' respectively.
1121
tempFormat[1] = format[i];
1122
tempFormat[2] = format[i + 1];
1123
tempFormat[3] = format[i + 2];
1124
tempFormat[4] = '\0';
1125
snprintf(tempStr, sizeof(tempStr), tempFormat, PARAM(param++));
1132
snprintf(tempStr, sizeof(tempStr), "%08x", PARAM(param++));
1146
// Just in case there were embedded strings that had \n's.
1147
if (!result.empty() && result[result.size() - 1] == '\n')
1148
result.resize(result.size() - 1);
1151
INFO_LOG(SCEKERNEL, "sceKernelPrintf: %s", result.c_str());
1153
ERROR_LOG(SCEKERNEL, "UNIMPL sceKernelPrintf(%s, %08x, %08x, %08x)", format.c_str(), PARAM(1), PARAM(2), PARAM(3));
1157
static int sceKernelSetCompiledSdkVersion(int sdkVersion) {
1158
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1159
bool validSDK = false;
1160
switch (sdkMainVersion) {
1182
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion unknown SDK: %x", sdkVersion);
1185
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion(%08x)", sdkVersion);
1186
sdkVersion_ = sdkVersion;
1187
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1191
static int sceKernelSetCompiledSdkVersion370(int sdkVersion) {
1192
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1193
if (sdkMainVersion != 0x03070000) {
1194
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion370 unknown SDK: %x", sdkVersion);
1197
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion370(%08x)", sdkVersion);
1198
sdkVersion_ = sdkVersion;
1199
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1203
static int sceKernelSetCompiledSdkVersion380_390(int sdkVersion) {
1204
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1205
if (sdkMainVersion != 0x03080000 && sdkMainVersion != 0x03090000) {
1206
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion380_390 unknown SDK: %x", sdkVersion);
1207
sdkVersion_ = sdkVersion;
1208
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1211
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion380_390(%08x)", sdkVersion);
1212
sdkVersion_ = sdkVersion;
1213
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1217
static int sceKernelSetCompiledSdkVersion395(int sdkVersion) {
1218
int sdkMainVersion = sdkVersion & 0xFFFFFF00;
1219
if (sdkMainVersion != 0x04000000
1220
&& sdkMainVersion != 0x04000100
1221
&& sdkMainVersion != 0x04000500
1222
&& sdkMainVersion != 0x03090500
1223
&& sdkMainVersion != 0x03090600) {
1224
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion395 unknown SDK: %x", sdkVersion);
1227
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion395(%08x)", sdkVersion);
1228
sdkVersion_ = sdkVersion;
1229
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1233
static int sceKernelSetCompiledSdkVersion600_602(int sdkVersion) {
1234
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1235
if (sdkMainVersion != 0x06010000
1236
&& sdkMainVersion != 0x06000000
1237
&& sdkMainVersion != 0x06020000) {
1238
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion600_602 unknown SDK: %x", sdkVersion);
1241
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion600_602(%08x)", sdkVersion);
1242
sdkVersion_ = sdkVersion;
1243
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1247
static int sceKernelSetCompiledSdkVersion500_505(int sdkVersion)
1249
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1250
if (sdkMainVersion != 0x05000000
1251
&& sdkMainVersion != 0x05050000) {
1252
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion500_505 unknown SDK: %x", sdkVersion);
1255
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion500_505(%08x)", sdkVersion);
1256
sdkVersion_ = sdkVersion;
1257
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1261
static int sceKernelSetCompiledSdkVersion401_402(int sdkVersion) {
1262
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1263
if (sdkMainVersion != 0x04010000
1264
&& sdkMainVersion != 0x04020000) {
1265
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion401_402 unknown SDK: %x", sdkVersion);
1268
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion401_402(%08x)", sdkVersion);
1269
sdkVersion_ = sdkVersion;
1270
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1274
static int sceKernelSetCompiledSdkVersion507(int sdkVersion) {
1275
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1276
if (sdkMainVersion != 0x05070000) {
1277
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion507 unknown SDK: %x", sdkVersion);
1280
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion507(%08x)", sdkVersion);
1281
sdkVersion_ = sdkVersion;
1282
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1286
static int sceKernelSetCompiledSdkVersion603_605(int sdkVersion) {
1287
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1288
if (sdkMainVersion != 0x06040000
1289
&& sdkMainVersion != 0x06030000
1290
&& sdkMainVersion != 0x06050000) {
1291
WARN_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion603_605 unknown SDK: %x", sdkVersion);
1294
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion603_605(%08x)", sdkVersion);
1295
sdkVersion_ = sdkVersion;
1296
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1300
static int sceKernelSetCompiledSdkVersion606(int sdkVersion) {
1301
int sdkMainVersion = sdkVersion & 0xFFFF0000;
1302
if (sdkMainVersion != 0x06060000) {
1303
ERROR_LOG_REPORT(SCEKERNEL, "sceKernelSetCompiledSdkVersion606 unknown SDK: %x (would crash)", sdkVersion);
1306
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompiledSdkVersion606(%08x)", sdkVersion);
1307
sdkVersion_ = sdkVersion;
1308
flags_ |= SCE_KERNEL_HASCOMPILEDSDKVERSION;
1312
int sceKernelGetCompiledSdkVersion() {
1313
if (!(flags_ & SCE_KERNEL_HASCOMPILEDSDKVERSION))
1318
static int sceKernelSetCompilerVersion(int version) {
1319
DEBUG_LOG(SCEKERNEL, "sceKernelSetCompilerVersion(%08x)", version);
1320
compilerVersion_ = version;
1321
flags_ |= SCE_KERNEL_HASCOMPILERVERSION;
1325
KernelObject *__KernelMemoryFPLObject()
1330
KernelObject *__KernelMemoryVPLObject()
1335
KernelObject *__KernelMemoryPMBObject()
1337
// TODO: We could theoretically handle kernelMemory too, but we don't support that now anyway.
1338
return new PartitionMemoryBlock(&userMemory, "", 0, PSP_SMEM_Low, 0);
1341
// VPL = variable length memory pool
1343
enum SceKernelVplAttr
1345
PSP_VPL_ATTR_FIFO = 0x0000,
1346
PSP_VPL_ATTR_PRIORITY = 0x0100,
1347
PSP_VPL_ATTR_SMALLEST = 0x0200,
1348
PSP_VPL_ATTR_MASK_ORDER = 0x0300,
1350
PSP_VPL_ATTR_HIGHMEM = 0x4000,
1351
PSP_VPL_ATTR_KNOWN = PSP_VPL_ATTR_FIFO | PSP_VPL_ATTR_PRIORITY | PSP_VPL_ATTR_SMALLEST | PSP_VPL_ATTR_HIGHMEM,
1354
static bool __KernelUnlockVplForThread(VPL *vpl, VplWaitingThread &threadInfo, u32 &error, int result, bool &wokeThreads) {
1355
const SceUID threadID = threadInfo.threadID;
1356
if (!HLEKernel::VerifyWait(threadID, WAITTYPE_VPL, vpl->GetUID())) {
1360
// If result is an error code, we're just letting it go.
1362
int size = (int) __KernelGetWaitValue(threadID, error);
1364
// An older savestate may have an invalid header, use the block allocator in that case.
1366
if (vpl->header.IsValid()) {
1367
addr = vpl->header->Allocate(size);
1369
// Padding (normally used to track the allocation.)
1370
u32 allocSize = size + 8;
1371
addr = vpl->alloc.Alloc(allocSize, true);
1373
if (addr != (u32) -1) {
1374
Memory::Write_U32(addr, threadInfo.addrPtr);
1380
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
1381
if (timeoutPtr != 0 && vplWaitTimer != -1) {
1382
// Remove any event for this thread.
1383
s64 cyclesLeft = CoreTiming::UnscheduleEvent(vplWaitTimer, threadID);
1384
Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
1387
__KernelResumeThreadFromWait(threadID, result);
1392
void __KernelVplBeginCallback(SceUID threadID, SceUID prevCallbackId)
1394
auto result = HLEKernel::WaitBeginCallback<VPL, WAITTYPE_VPL, VplWaitingThread>(threadID, prevCallbackId, vplWaitTimer);
1395
if (result == HLEKernel::WAIT_CB_SUCCESS)
1396
DEBUG_LOG(SCEKERNEL, "sceKernelAllocateVplCB: Suspending vpl wait for callback");
1397
else if (result == HLEKernel::WAIT_CB_BAD_WAIT_DATA)
1398
ERROR_LOG_REPORT(SCEKERNEL, "sceKernelAllocateVplCB: wait not found to pause for callback");
1400
WARN_LOG_REPORT(SCEKERNEL, "sceKernelAllocateVplCB: beginning callback with bad wait id?");
1403
void __KernelVplEndCallback(SceUID threadID, SceUID prevCallbackId)
1405
auto result = HLEKernel::WaitEndCallback<VPL, WAITTYPE_VPL, VplWaitingThread>(threadID, prevCallbackId, vplWaitTimer, __KernelUnlockVplForThread);
1406
if (result == HLEKernel::WAIT_CB_RESUMED_WAIT)
1407
DEBUG_LOG(SCEKERNEL, "sceKernelReceiveMbxCB: Resuming mbx wait from callback");
1410
static bool __VplThreadSortPriority(VplWaitingThread thread1, VplWaitingThread thread2)
1412
return __KernelThreadSortPriority(thread1.threadID, thread2.threadID);
1415
static bool __KernelClearVplThreads(VPL *vpl, int reason)
1418
bool wokeThreads = false;
1419
for (auto iter = vpl->waitingThreads.begin(), end = vpl->waitingThreads.end(); iter != end; ++iter)
1420
__KernelUnlockVplForThread(vpl, *iter, error, reason, wokeThreads);
1421
vpl->waitingThreads.clear();
1426
static void __KernelSortVplThreads(VPL *vpl)
1428
// Remove any that are no longer waiting.
1429
SceUID uid = vpl->GetUID();
1430
HLEKernel::CleanupWaitingThreads(WAITTYPE_VPL, uid, vpl->waitingThreads);
1432
if ((vpl->nv.attr & PSP_VPL_ATTR_PRIORITY) != 0)
1433
std::stable_sort(vpl->waitingThreads.begin(), vpl->waitingThreads.end(), __VplThreadSortPriority);
1436
SceUID sceKernelCreateVpl(const char *name, int partition, u32 attr, u32 vplSize, u32 optPtr)
1440
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid name", SCE_KERNEL_ERROR_ERROR);
1441
return SCE_KERNEL_ERROR_ERROR;
1443
if (partition < 1 || partition > 9 || partition == 7)
1445
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, partition);
1446
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
1448
// We only support user right now.
1449
if (partition != 2 && partition != 6)
1451
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_PERM, partition);
1452
return SCE_KERNEL_ERROR_ILLEGAL_PERM;
1454
if (((attr & ~PSP_VPL_ATTR_KNOWN) & ~0xFF) != 0)
1456
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
1457
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
1461
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): invalid size", SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE);
1462
return SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE;
1464
// Block Allocator seems to A-OK this, let's stop it here.
1465
if (vplSize >= 0x80000000)
1467
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateVpl(): way too big size", SCE_KERNEL_ERROR_NO_MEMORY);
1468
return SCE_KERNEL_ERROR_NO_MEMORY;
1471
// Can't have that little space in a Vpl, sorry.
1472
if (vplSize <= 0x30)
1474
vplSize = (vplSize + 7) & ~7;
1476
// We ignore the upalign to 256 and do it ourselves by 8.
1477
u32 allocSize = vplSize;
1478
u32 memBlockPtr = userMemory.Alloc(allocSize, (attr & PSP_VPL_ATTR_HIGHMEM) != 0, "VPL");
1479
if (memBlockPtr == (u32)-1)
1481
ERROR_LOG(SCEKERNEL, "sceKernelCreateVpl(): Failed to allocate %i bytes of pool data", vplSize);
1482
return SCE_KERNEL_ERROR_NO_MEMORY;
1486
SceUID id = kernelObjects.Create(vpl);
1488
strncpy(vpl->nv.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
1489
vpl->nv.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
1490
vpl->nv.attr = attr;
1491
vpl->nv.size = sizeof(vpl->nv);
1492
vpl->nv.poolSize = vplSize - 0x20;
1493
vpl->nv.numWaitThreads = 0;
1494
vpl->nv.freeSize = vpl->nv.poolSize;
1496
// A vpl normally has accounting stuff in the first 32 bytes.
1497
vpl->address = memBlockPtr + 0x20;
1498
vpl->alloc.Init(vpl->address, vpl->nv.poolSize);
1500
vpl->header = PSPPointer<SceKernelVplHeader>::Create(memBlockPtr);
1501
vpl->header->Init(memBlockPtr, vplSize);
1503
DEBUG_LOG(SCEKERNEL, "%x=sceKernelCreateVpl(\"%s\", block=%i, attr=%i, size=%i)",
1504
id, name, partition, vpl->nv.attr, vpl->nv.poolSize);
1508
u32 size = Memory::Read_U32(optPtr);
1510
WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateVpl(): unsupported options parameter, size = %d", size);
1516
int sceKernelDeleteVpl(SceUID uid)
1518
DEBUG_LOG(SCEKERNEL, "sceKernelDeleteVpl(%i)", uid);
1520
VPL *vpl = kernelObjects.Get<VPL>(uid, error);
1523
bool wokeThreads = __KernelClearVplThreads(vpl, SCE_KERNEL_ERROR_WAIT_DELETE);
1525
hleReSchedule("vpl deleted");
1527
userMemory.Free(vpl->address);
1528
kernelObjects.Destroy<VPL>(uid);
1535
// Returns false for invalid parameters (e.g. don't check callbacks, etc.)
1536
// Successful allocation is indicated by error == 0.
1537
static bool __KernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 &error, bool trying, const char *funcname) {
1538
VPL *vpl = kernelObjects.Get<VPL>(uid, error);
1540
if (size == 0 || size > (u32) vpl->nv.poolSize) {
1541
WARN_LOG(SCEKERNEL, "%s(vpl=%i, size=%i, ptrout=%08x): invalid size", funcname, uid, size, addrPtr);
1542
error = SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE;
1546
VERBOSE_LOG(SCEKERNEL, "%s(vpl=%i, size=%i, ptrout=%08x)", funcname, uid, size, addrPtr);
1548
// For some reason, try doesn't follow the same rules...
1549
if (!trying && (vpl->nv.attr & PSP_VPL_ATTR_MASK_ORDER) == PSP_VPL_ATTR_FIFO)
1551
__KernelSortVplThreads(vpl);
1552
if (!vpl->waitingThreads.empty())
1554
// Can't allocate, blocked by FIFO queue.
1555
error = SCE_KERNEL_ERROR_NO_MEMORY;
1560
// Allocate using the header only for newer vpls (older come from savestates.)
1562
if (vpl->header.IsValid()) {
1563
addr = vpl->header->Allocate(size);
1565
// Padding (normally used to track the allocation.)
1566
u32 allocSize = size + 8;
1567
addr = vpl->alloc.Alloc(allocSize, true);
1569
if (addr != (u32) -1) {
1570
Memory::Write_U32(addr, addrPtr);
1573
error = SCE_KERNEL_ERROR_NO_MEMORY;
1582
void __KernelVplTimeout(u64 userdata, int cyclesLate) {
1583
SceUID threadID = (SceUID) userdata;
1585
SceUID uid = __KernelGetWaitID(threadID, WAITTYPE_VPL, error);
1587
HLEKernel::WaitExecTimeout<VPL, WAITTYPE_VPL>(threadID);
1589
// If in FIFO mode, that may have cleared another thread to wake up.
1590
VPL *vpl = kernelObjects.Get<VPL>(uid, error);
1591
if (vpl && (vpl->nv.attr & PSP_VPL_ATTR_MASK_ORDER) == PSP_VPL_ATTR_FIFO) {
1593
std::vector<VplWaitingThread>::iterator iter = vpl->waitingThreads.begin();
1594
// Unlock every waiting thread until the first that must still wait.
1595
while (iter != vpl->waitingThreads.end() && __KernelUnlockVplForThread(vpl, *iter, error, 0, wokeThreads)) {
1596
vpl->waitingThreads.erase(iter);
1597
iter = vpl->waitingThreads.begin();
1602
static void __KernelSetVplTimeout(u32 timeoutPtr)
1604
if (timeoutPtr == 0 || vplWaitTimer == -1)
1607
int micro = (int) Memory::Read_U32(timeoutPtr);
1609
// This happens to be how the hardware seems to time things.
1612
// Yes, this 7 is reproducible. 6 is (a lot) longer than 7.
1613
else if (micro == 7)
1615
else if (micro <= 215)
1618
CoreTiming::ScheduleEvent(usToCycles(micro), vplWaitTimer, __KernelGetCurThread());
1621
int sceKernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr)
1624
if (__KernelAllocateVpl(uid, size, addrPtr, error, false, __FUNCTION__))
1626
VPL *vpl = kernelObjects.Get<VPL>(uid, ignore);
1627
if (error == SCE_KERNEL_ERROR_NO_MEMORY)
1629
if (timeoutPtr != 0 && Memory::Read_U32(timeoutPtr) == 0)
1630
return SCE_KERNEL_ERROR_WAIT_TIMEOUT;
1634
SceUID threadID = __KernelGetCurThread();
1635
HLEKernel::RemoveWaitingThread(vpl->waitingThreads, threadID);
1636
VplWaitingThread waiting = {threadID, addrPtr};
1637
vpl->waitingThreads.push_back(waiting);
1640
__KernelSetVplTimeout(timeoutPtr);
1641
__KernelWaitCurThread(WAITTYPE_VPL, uid, size, timeoutPtr, false, "vpl waited");
1643
// If anyone else was waiting, the allocation causes a delay.
1644
else if (error == 0 && !vpl->waitingThreads.empty())
1645
return hleDelayResult(error, "vpl allocated", 50);
1650
int sceKernelAllocateVplCB(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr)
1653
if (__KernelAllocateVpl(uid, size, addrPtr, error, false, __FUNCTION__))
1655
hleCheckCurrentCallbacks();
1657
VPL *vpl = kernelObjects.Get<VPL>(uid, ignore);
1658
if (error == SCE_KERNEL_ERROR_NO_MEMORY)
1660
if (timeoutPtr != 0 && Memory::Read_U32(timeoutPtr) == 0)
1661
return SCE_KERNEL_ERROR_WAIT_TIMEOUT;
1665
SceUID threadID = __KernelGetCurThread();
1666
HLEKernel::RemoveWaitingThread(vpl->waitingThreads, threadID);
1667
VplWaitingThread waiting = {threadID, addrPtr};
1668
vpl->waitingThreads.push_back(waiting);
1671
__KernelSetVplTimeout(timeoutPtr);
1672
__KernelWaitCurThread(WAITTYPE_VPL, uid, size, timeoutPtr, true, "vpl waited");
1674
// If anyone else was waiting, the allocation causes a delay.
1675
else if (error == 0 && !vpl->waitingThreads.empty())
1676
return hleDelayResult(error, "vpl allocated", 50);
1681
int sceKernelTryAllocateVpl(SceUID uid, u32 size, u32 addrPtr)
1684
__KernelAllocateVpl(uid, size, addrPtr, error, true, __FUNCTION__);
1688
int sceKernelFreeVpl(SceUID uid, u32 addr) {
1689
if (addr && !Memory::IsValidAddress(addr)) {
1690
WARN_LOG(SCEKERNEL, "%08x=sceKernelFreeVpl(%i, %08x): Invalid address", SCE_KERNEL_ERROR_ILLEGAL_ADDR, uid, addr);
1691
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
1694
VERBOSE_LOG(SCEKERNEL, "sceKernelFreeVpl(%i, %08x)", uid, addr);
1696
VPL *vpl = kernelObjects.Get<VPL>(uid, error);
1699
// Free using the header for newer vpls (not old savestates.)
1700
if (vpl->header.IsValid()) {
1701
freed = vpl->header->Free(addr);
1703
freed = vpl->alloc.FreeExact(addr);
1707
__KernelSortVplThreads(vpl);
1709
bool wokeThreads = false;
1711
for (auto iter = vpl->waitingThreads.begin(), end = vpl->waitingThreads.end(); iter != end; ++iter) {
1712
if (__KernelUnlockVplForThread(vpl, *iter, error, 0, wokeThreads)) {
1713
vpl->waitingThreads.erase(iter);
1716
// In FIFO, we stop at the first one that can't wake.
1717
else if ((vpl->nv.attr & PSP_VPL_ATTR_MASK_ORDER) == PSP_VPL_ATTR_FIFO)
1722
hleReSchedule("vpl freed");
1727
WARN_LOG(SCEKERNEL, "%08x=sceKernelFreeVpl(%i, %08x): Unable to free", SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCK, uid, addr);
1728
return SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCK;
1735
int sceKernelCancelVpl(SceUID uid, u32 numWaitThreadsPtr)
1738
VPL *vpl = kernelObjects.Get<VPL>(uid, error);
1741
DEBUG_LOG(SCEKERNEL, "sceKernelCancelVpl(%i, %08x)", uid, numWaitThreadsPtr);
1742
vpl->nv.numWaitThreads = (int) vpl->waitingThreads.size();
1743
if (Memory::IsValidAddress(numWaitThreadsPtr))
1744
Memory::Write_U32(vpl->nv.numWaitThreads, numWaitThreadsPtr);
1746
bool wokeThreads = __KernelClearVplThreads(vpl, SCE_KERNEL_ERROR_WAIT_CANCEL);
1748
hleReSchedule("vpl canceled");
1754
DEBUG_LOG(SCEKERNEL, "sceKernelCancelVpl(%i, %08x): invalid vpl", uid, numWaitThreadsPtr);
1759
int sceKernelReferVplStatus(SceUID uid, u32 infoPtr) {
1761
VPL *vpl = kernelObjects.Get<VPL>(uid, error);
1763
DEBUG_LOG(SCEKERNEL, "sceKernelReferVplStatus(%i, %08x)", uid, infoPtr);
1765
__KernelSortVplThreads(vpl);
1766
vpl->nv.numWaitThreads = (int) vpl->waitingThreads.size();
1767
if (vpl->header.IsValid()) {
1768
vpl->nv.freeSize = vpl->header->FreeSize();
1770
vpl->nv.freeSize = vpl->alloc.GetTotalFreeBytes();
1772
if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr) != 0) {
1773
Memory::WriteStruct(infoPtr, &vpl->nv);
1781
static u32 AllocMemoryBlock(const char *pname, u32 type, u32 size, u32 paramsAddr) {
1782
if (Memory::IsValidAddress(paramsAddr) && Memory::Read_U32(paramsAddr) != 4) {
1783
ERROR_LOG_REPORT(SCEKERNEL, "AllocMemoryBlock(%s): unsupported params size %d", pname, Memory::Read_U32(paramsAddr));
1784
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
1786
if (type != PSP_SMEM_High && type != PSP_SMEM_Low) {
1787
ERROR_LOG_REPORT(SCEKERNEL, "AllocMemoryBlock(%s): unsupported type %d", pname, type);
1788
return SCE_KERNEL_ERROR_ILLEGAL_MEMBLOCKTYPE;
1791
WARN_LOG_REPORT(SCEKERNEL, "AllocMemoryBlock(%s): invalid size %x", pname, size);
1792
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
1794
if (pname == NULL) {
1795
ERROR_LOG_REPORT(SCEKERNEL, "AllocMemoryBlock(): NULL name");
1796
return SCE_KERNEL_ERROR_ERROR;
1799
PartitionMemoryBlock *block = new PartitionMemoryBlock(&userMemory, pname, size, (MemblockType)type, 0);
1800
if (!block->IsValid())
1803
ERROR_LOG(SCEKERNEL, "AllocMemoryBlock(%s, %i, %08x, %08x): allocation failed", pname, type, size, paramsAddr);
1804
return SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
1806
SceUID uid = kernelObjects.Create(block);
1808
INFO_LOG(SCEKERNEL,"%08x=AllocMemoryBlock(SysMemUserForUser_FE707FDF)(%s, %i, %08x, %08x)", uid, pname, type, size, paramsAddr);
1812
static u32 FreeMemoryBlock(u32 uid) {
1813
INFO_LOG(SCEKERNEL, "FreeMemoryBlock(%08x)", uid);
1814
return kernelObjects.Destroy<PartitionMemoryBlock>(uid);
1817
static u32 GetMemoryBlockPtr(u32 uid, u32 addr) {
1819
PartitionMemoryBlock *block = kernelObjects.Get<PartitionMemoryBlock>(uid, error);
1822
INFO_LOG(SCEKERNEL, "GetMemoryBlockPtr(%08x, %08x) = %08x", uid, addr, block->address);
1823
Memory::Write_U32(block->address, addr);
1828
ERROR_LOG(SCEKERNEL, "GetMemoryBlockPtr(%08x, %08x) failed", uid, addr);
1833
static u32 SysMemUserForUser_D8DE5C1E() {
1834
// Called by Evangelion Jo and return 0 here to go in-game.
1835
ERROR_LOG(SCEKERNEL,"UNIMPL SysMemUserForUser_D8DE5C1E()");
1839
static u32 SysMemUserForUser_ACBD88CA() {
1840
ERROR_LOG_REPORT_ONCE(SysMemUserForUser_ACBD88CA, SCEKERNEL, "UNIMPL SysMemUserForUser_ACBD88CA()");
1844
static u32 SysMemUserForUser_945E45DA() {
1845
// Called by Evangelion Jo and expected return 0 here.
1846
ERROR_LOG_REPORT_ONCE(SysMemUserForUser945E45DA, SCEKERNEL, "UNIMPL SysMemUserForUser_945E45DA()");
1852
PSP_ERROR_UNKNOWN_TLSPL_ID = 0x800201D0,
1853
PSP_ERROR_TOO_MANY_TLSPL = 0x800201D1,
1854
PSP_ERROR_TLSPL_IN_USE = 0x800201D2,
1859
// TODO: Complete untested guesses.
1860
PSP_TLSPL_ATTR_FIFO = 0,
1861
PSP_TLSPL_ATTR_PRIORITY = 0x100,
1862
PSP_TLSPL_ATTR_HIGHMEM = 0x4000,
1863
PSP_TLSPL_ATTR_KNOWN = PSP_TLSPL_ATTR_HIGHMEM | PSP_TLSPL_ATTR_PRIORITY | PSP_TLSPL_ATTR_FIFO,
1875
u32_le numWaitThreads;
1878
struct TLSPL : public KernelObject
1880
const char *GetName() override { return ntls.name; }
1881
const char *GetTypeName() override { return "TLS"; }
1882
static u32 GetMissingErrorCode() { return PSP_ERROR_UNKNOWN_TLSPL_ID; }
1883
static int GetStaticIDType() { return SCE_KERNEL_TMID_Tlspl; }
1884
int GetIDType() const override { return SCE_KERNEL_TMID_Tlspl; }
1886
TLSPL() : next(0) {}
1888
void DoState(PointerWrap &p) override
1890
auto s = p.Section("TLS", 1, 2);
1900
p.Do(waitingThreads);
1908
std::vector<SceUID> waitingThreads;
1910
std::vector<SceUID> usage;
1913
KernelObject *__KernelTlsplObject()
1918
static void __KernelSortTlsplThreads(TLSPL *tls)
1920
// Remove any that are no longer waiting.
1921
SceUID uid = tls->GetUID();
1922
HLEKernel::CleanupWaitingThreads(WAITTYPE_TLSPL, uid, tls->waitingThreads);
1924
if ((tls->ntls.attr & PSP_FPL_ATTR_PRIORITY) != 0)
1925
std::stable_sort(tls->waitingThreads.begin(), tls->waitingThreads.end(), __KernelThreadSortPriority);
1928
int __KernelFreeTls(TLSPL *tls, SceUID threadID)
1930
// Find the current thread's block.
1932
for (size_t i = 0; i < tls->ntls.totalBlocks; ++i)
1934
if (tls->usage[i] == threadID)
1936
freeBlock = (int) i;
1941
if (freeBlock != -1)
1943
SceUID uid = tls->GetUID();
1945
u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1);
1946
u32 freedAddress = tls->address + freeBlock * alignedSize;
1948
// Whenever freeing a block, clear it (even if it's not going to wake anyone.)
1949
Memory::Memset(freedAddress, 0, tls->ntls.blockSize);
1951
// First, let's remove the end check for the freeing thread.
1952
auto freeingLocked = tlsplThreadEndChecks.equal_range(threadID);
1953
for (TlsplMap::iterator iter = freeingLocked.first; iter != freeingLocked.second; ++iter)
1955
if (iter->second == uid)
1957
tlsplThreadEndChecks.erase(iter);
1962
__KernelSortTlsplThreads(tls);
1963
while (!tls->waitingThreads.empty())
1965
SceUID waitingThreadID = tls->waitingThreads[0];
1966
tls->waitingThreads.erase(tls->waitingThreads.begin());
1968
// This thread must've been woken up.
1969
if (!HLEKernel::VerifyWait(waitingThreadID, WAITTYPE_TLSPL, uid))
1972
// Otherwise, if there was a thread waiting, we were full, so this newly freed one is theirs.
1973
tls->usage[freeBlock] = waitingThreadID;
1974
__KernelResumeThreadFromWait(waitingThreadID, freedAddress);
1976
// Gotta watch the thread to quit as well, since they've allocated now.
1977
tlsplThreadEndChecks.insert(std::make_pair(waitingThreadID, uid));
1979
// No need to continue or free it, we're done.
1983
// No one was waiting, so now we can really free it.
1984
tls->usage[freeBlock] = 0;
1985
++tls->ntls.freeBlocks;
1988
// We say "okay" even though nothing was freed.
1993
void __KernelTlsplThreadEnd(SceUID threadID)
1997
// It wasn't waiting, was it?
1998
SceUID waitingTlsID = __KernelGetWaitID(threadID, WAITTYPE_TLSPL, error);
2001
TLSPL *tls = kernelObjects.Get<TLSPL>(waitingTlsID, error);
2003
tls->waitingThreads.erase(std::remove(tls->waitingThreads.begin(), tls->waitingThreads.end(), threadID), tls->waitingThreads.end());
2006
// Unlock all pools the thread had locked.
2007
auto locked = tlsplThreadEndChecks.equal_range(threadID);
2008
for (TlsplMap::iterator iter = locked.first; iter != locked.second; ++iter)
2010
SceUID tlsID = iter->second;
2011
TLSPL *tls = kernelObjects.Get<TLSPL>(tlsID, error);
2015
__KernelFreeTls(tls, threadID);
2017
// Restart the loop, freeing mutated it.
2018
locked = tlsplThreadEndChecks.equal_range(threadID);
2019
iter = locked.first;
2020
if (locked.first == locked.second)
2024
tlsplThreadEndChecks.erase(locked.first, locked.second);
2027
SceUID sceKernelCreateTlspl(const char *name, u32 partition, u32 attr, u32 blockSize, u32 count, u32 optionsPtr)
2031
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid name", SCE_KERNEL_ERROR_NO_MEMORY);
2032
return SCE_KERNEL_ERROR_NO_MEMORY;
2034
if ((attr & ~PSP_TLSPL_ATTR_KNOWN) >= 0x100)
2036
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
2037
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
2039
if (partition < 1 || partition > 9 || partition == 7)
2041
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT, partition);
2042
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
2044
// We only support user right now.
2045
if (partition != 2 && partition != 6)
2047
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid partition %d", SCE_KERNEL_ERROR_ILLEGAL_PERM, partition);
2048
return SCE_KERNEL_ERROR_ILLEGAL_PERM;
2051
// There's probably a simpler way to get this same basic formula...
2052
// This is based on results from a PSP.
2053
bool illegalMemSize = blockSize == 0 || count == 0;
2054
if (!illegalMemSize && (u64) blockSize > ((0x100000000ULL / (u64) count) - 4ULL))
2055
illegalMemSize = true;
2056
if (!illegalMemSize && (u64) count >= 0x100000000ULL / (((u64) blockSize + 3ULL) & ~3ULL))
2057
illegalMemSize = true;
2060
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): invalid blockSize/count", SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE);
2061
return SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE;
2065
for (int i = 0; i < TLSPL_NUM_INDEXES; ++i)
2066
if (tlsplUsedIndexes[i] == false)
2074
WARN_LOG_REPORT(SCEKERNEL, "%08x=sceKernelCreateTlspl(): ran out of indexes for TLS pools", PSP_ERROR_TOO_MANY_TLSPL);
2075
return PSP_ERROR_TOO_MANY_TLSPL;
2078
// Unless otherwise specified, we align to 4 bytes (a mips word.)
2080
if (optionsPtr != 0)
2082
u32 size = Memory::Read_U32(optionsPtr);
2084
WARN_LOG_REPORT(SCEKERNEL, "sceKernelCreateTlspl(%s) unsupported options parameter, size = %d", name, size);
2086
alignment = Memory::Read_U32(optionsPtr + 4);
2088
// Note that 0 intentionally is allowed.
2089
if ((alignment & (alignment - 1)) != 0)
2091
ERROR_LOG_REPORT(SCEKERNEL, "sceKernelCreateTlspl(%s): alignment is not a power of 2: %d", name, alignment);
2092
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
2094
// This goes for 0, 1, and 2. Can't have less than 4 byte alignment.
2099
// Upalign. Strangely, the sceKernelReferTlsplStatus value is the original.
2100
u32 alignedSize = (blockSize + alignment - 1) & ~(alignment - 1);
2102
u32 totalSize = alignedSize * count;
2103
u32 blockPtr = userMemory.Alloc(totalSize, (attr & PSP_TLSPL_ATTR_HIGHMEM) != 0, name);
2105
userMemory.ListBlocks();
2108
if (blockPtr == (u32) -1)
2110
ERROR_LOG(SCEKERNEL, "%08x=sceKernelCreateTlspl(%s, %d, %08x, %d, %d, %08x): failed to allocate memory", SCE_KERNEL_ERROR_NO_MEMORY, name, partition, attr, blockSize, count, optionsPtr);
2111
return SCE_KERNEL_ERROR_NO_MEMORY;
2114
TLSPL *tls = new TLSPL();
2115
SceUID id = kernelObjects.Create(tls);
2117
tls->ntls.size = sizeof(tls->ntls);
2118
strncpy(tls->ntls.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
2119
tls->ntls.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
2120
tls->ntls.attr = attr;
2121
tls->ntls.index = index;
2122
tlsplUsedIndexes[index] = true;
2123
tls->ntls.blockSize = blockSize;
2124
tls->ntls.totalBlocks = count;
2125
tls->ntls.freeBlocks = count;
2126
tls->ntls.numWaitThreads = 0;
2127
tls->address = blockPtr;
2128
tls->alignment = alignment;
2129
tls->usage.resize(count, 0);
2131
WARN_LOG(SCEKERNEL, "%08x=sceKernelCreateTlspl(%s, %d, %08x, %d, %d, %08x)", id, name, partition, attr, blockSize, count, optionsPtr);
2136
int sceKernelDeleteTlspl(SceUID uid)
2139
TLSPL *tls = kernelObjects.Get<TLSPL>(uid, error);
2143
for (SceUID threadID : tls->usage)
2145
if (threadID != 0 && threadID != __KernelGetCurThread())
2150
error = PSP_ERROR_TLSPL_IN_USE;
2151
WARN_LOG(SCEKERNEL, "%08x=sceKernelDeleteTlspl(%08x): in use", error, uid);
2155
WARN_LOG(SCEKERNEL, "sceKernelDeleteTlspl(%08x)", uid);
2157
for (SceUID threadID : tls->waitingThreads)
2158
HLEKernel::ResumeFromWait(threadID, WAITTYPE_TLSPL, uid, 0);
2159
hleReSchedule("deleted tlspl");
2161
userMemory.Free(tls->address);
2162
tlsplUsedIndexes[tls->ntls.index] = false;
2163
kernelObjects.Destroy<TLSPL>(uid);
2166
ERROR_LOG(SCEKERNEL, "%08x=sceKernelDeleteTlspl(%08x): bad tlspl", error, uid);
2170
int sceKernelGetTlsAddr(SceUID uid)
2172
// TODO: Allocate downward if PSP_TLSPL_ATTR_HIGHMEM?
2173
DEBUG_LOG(SCEKERNEL, "sceKernelGetTlsAddr(%08x)", uid);
2175
if (!__KernelIsDispatchEnabled() || __IsInInterrupt())
2179
TLSPL *tls = kernelObjects.Get<TLSPL>(uid, error);
2182
SceUID threadID = __KernelGetCurThread();
2183
int allocBlock = -1;
2184
bool needsClear = false;
2186
// If the thread already has one, return it.
2187
for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i)
2189
if (tls->usage[i] == threadID)
2190
allocBlock = (int) i;
2193
if (allocBlock == -1)
2195
for (size_t i = 0; i < tls->ntls.totalBlocks && allocBlock == -1; ++i)
2197
// The PSP doesn't give the same block out twice in a row, even if freed.
2198
if (tls->usage[tls->next] == 0)
2199
allocBlock = tls->next;
2200
tls->next = (tls->next + 1) % tls->ntls.totalBlocks;
2203
if (allocBlock != -1)
2205
tls->usage[allocBlock] = threadID;
2206
tlsplThreadEndChecks.insert(std::make_pair(threadID, uid));
2207
--tls->ntls.freeBlocks;
2212
if (allocBlock == -1)
2214
tls->waitingThreads.push_back(threadID);
2215
__KernelWaitCurThread(WAITTYPE_TLSPL, uid, 1, 0, false, "allocate tls");
2219
u32 alignedSize = (tls->ntls.blockSize + tls->alignment - 1) & ~(tls->alignment - 1);
2220
u32 allocAddress = tls->address + allocBlock * alignedSize;
2222
// We clear the blocks upon first allocation (and also when they are freed, both are necessary.)
2224
Memory::Memset(allocAddress, 0, tls->ntls.blockSize);
2226
return allocAddress;
2232
// Parameters are an educated guess.
2233
int sceKernelFreeTlspl(SceUID uid)
2235
WARN_LOG(SCEKERNEL, "UNIMPL sceKernelFreeTlspl(%08x)", uid);
2237
TLSPL *tls = kernelObjects.Get<TLSPL>(uid, error);
2240
SceUID threadID = __KernelGetCurThread();
2241
return __KernelFreeTls(tls, threadID);
2247
int sceKernelReferTlsplStatus(SceUID uid, u32 infoPtr)
2249
DEBUG_LOG(SCEKERNEL, "sceKernelReferTlsplStatus(%08x, %08x)", uid, infoPtr);
2251
TLSPL *tls = kernelObjects.Get<TLSPL>(uid, error);
2254
// Update the waiting threads in case of deletions, etc.
2255
__KernelSortTlsplThreads(tls);
2256
tls->ntls.numWaitThreads = (int) tls->waitingThreads.size();
2258
if (Memory::Read_U32(infoPtr) != 0)
2259
Memory::WriteStruct(infoPtr, &tls->ntls);
2266
const HLEFunction SysMemUserForUser[] = {
2267
{0XA291F107, &WrapU_V<sceKernelMaxFreeMemSize>, "sceKernelMaxFreeMemSize", 'x', "" },
2268
{0XF919F628, &WrapU_V<sceKernelTotalFreeMemSize>, "sceKernelTotalFreeMemSize", 'x', "" },
2269
{0X3FC9AE6A, &WrapU_V<sceKernelDevkitVersion>, "sceKernelDevkitVersion", 'x', "" },
2270
{0X237DBD4F, &WrapI_ICIUU<sceKernelAllocPartitionMemory>, "sceKernelAllocPartitionMemory", 'i', "isixx"},
2271
{0XB6D61D02, &WrapI_I<sceKernelFreePartitionMemory>, "sceKernelFreePartitionMemory", 'i', "i" },
2272
{0X9D9A5BA1, &WrapU_I<sceKernelGetBlockHeadAddr>, "sceKernelGetBlockHeadAddr", 'x', "i" },
2273
{0X13A5ABEF, &WrapI_C<sceKernelPrintf>, "sceKernelPrintf", 'i', "s" },
2274
{0X7591C7DB, &WrapI_I<sceKernelSetCompiledSdkVersion>, "sceKernelSetCompiledSdkVersion", 'i', "i" },
2275
{0X342061E5, &WrapI_I<sceKernelSetCompiledSdkVersion370>, "sceKernelSetCompiledSdkVersion370", 'i', "i" },
2276
{0X315AD3A0, &WrapI_I<sceKernelSetCompiledSdkVersion380_390>, "sceKernelSetCompiledSdkVersion380_390", 'i', "i" },
2277
{0XEBD5C3E6, &WrapI_I<sceKernelSetCompiledSdkVersion395>, "sceKernelSetCompiledSdkVersion395", 'i', "i" },
2278
{0X057E7380, &WrapI_I<sceKernelSetCompiledSdkVersion401_402>, "sceKernelSetCompiledSdkVersion401_402", 'i', "i" },
2279
{0XF77D77CB, &WrapI_I<sceKernelSetCompilerVersion>, "sceKernelSetCompilerVersion", 'i', "i" },
2280
{0X91DE343C, &WrapI_I<sceKernelSetCompiledSdkVersion500_505>, "sceKernelSetCompiledSdkVersion500_505", 'i', "i" },
2281
{0X7893F79A, &WrapI_I<sceKernelSetCompiledSdkVersion507>, "sceKernelSetCompiledSdkVersion507", 'i', "i" },
2282
{0X35669D4C, &WrapI_I<sceKernelSetCompiledSdkVersion600_602>, "sceKernelSetCompiledSdkVersion600_602", 'i', "i" }, //??
2283
{0X1B4217BC, &WrapI_I<sceKernelSetCompiledSdkVersion603_605>, "sceKernelSetCompiledSdkVersion603_605", 'i', "i" },
2284
{0X358CA1BB, &WrapI_I<sceKernelSetCompiledSdkVersion606>, "sceKernelSetCompiledSdkVersion606", 'i', "i" },
2285
{0XFC114573, &WrapI_V<sceKernelGetCompiledSdkVersion>, "sceKernelGetCompiledSdkVersion", 'i', "" },
2286
{0X2A3E5280, nullptr, "sceKernelQueryMemoryInfo", '?', "" },
2287
{0XACBD88CA, &WrapU_V<SysMemUserForUser_ACBD88CA>, "SysMemUserForUser_ACBD88CA", 'x', "" },
2288
{0X945E45DA, &WrapU_V<SysMemUserForUser_945E45DA>, "SysMemUserForUser_945E45DA", 'x', "" },
2289
{0XA6848DF8, nullptr, "sceKernelSetUsersystemLibWork", '?', "" },
2290
{0X6231A71D, nullptr, "sceKernelSetPTRIG", '?', "" },
2291
{0X39F49610, nullptr, "sceKernelGetPTRIG", '?', "" },
2292
// Obscure raw block API
2293
{0XDB83A952, &WrapU_UU<GetMemoryBlockPtr>, "SysMemUserForUser_DB83A952", 'x', "xx" }, // GetMemoryBlockAddr
2294
{0X50F61D8A, &WrapU_U<FreeMemoryBlock>, "SysMemUserForUser_50F61D8A", 'x', "x" }, // FreeMemoryBlock
2295
{0XFE707FDF, &WrapU_CUUU<AllocMemoryBlock>, "SysMemUserForUser_FE707FDF", 'x', "sxxx" }, // AllocMemoryBlock
2296
{0XD8DE5C1E, &WrapU_V<SysMemUserForUser_D8DE5C1E>, "SysMemUserForUser_D8DE5C1E", 'x', "" },
2299
const HLEFunction SysMemForKernel[] = {
2300
{0x636C953B, nullptr, "SysMemForKernel_636c953b", '?', "" },
2301
{0xC9805775, nullptr, "SysMemForKernel_c9805775", '?', "" },
2302
{0x1C1FBFE7, nullptr, "SysMemForKernel_1c1fbfe7", '?', "" },
2305
void Register_SysMemForKernel() {
2306
RegisterModule("SysMemForKernel", ARRAY_SIZE(SysMemForKernel), SysMemForKernel);
2309
void Register_SysMemUserForUser() {
2310
RegisterModule("SysMemUserForUser", ARRAY_SIZE(SysMemUserForUser), SysMemUserForUser);