Implement sceKernelExtendThreadStack().
Fixes #1833. Implemented using fake nids instead of callbacks since it's simpler to get them (and waits and recursion) right.
This commit is contained in:
parent
0525906c70
commit
0fb5877d75
4 changed files with 151 additions and 42 deletions
|
@ -306,29 +306,29 @@ public:
|
|||
if (nt.attr & PSP_THREAD_ATTR_KERNEL)
|
||||
{
|
||||
// Allocate stacks for kernel threads (idle) in kernel RAM
|
||||
stackBlock = kernelMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
|
||||
currentStack.start = kernelMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
stackBlock = userMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
|
||||
currentStack.start = userMemory.Alloc(stackSize, true, (std::string("stack/") + nt.name).c_str());
|
||||
}
|
||||
if (stackBlock == (u32)-1)
|
||||
if (currentStack.start == (u32)-1)
|
||||
{
|
||||
stackBlock = 0;
|
||||
currentStack.start = 0;
|
||||
ERROR_LOG(HLE, "Failed to allocate stack for thread");
|
||||
return false;
|
||||
}
|
||||
|
||||
nt.initialStack = stackBlock;
|
||||
nt.initialStack = currentStack.start;
|
||||
nt.stackSize = stackSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FillStack() {
|
||||
// Fill the stack.
|
||||
Memory::Memset(stackBlock, 0xFF, nt.stackSize);
|
||||
context.r[MIPS_REG_SP] = stackBlock + nt.stackSize;
|
||||
stackEnd = context.r[MIPS_REG_SP];
|
||||
Memory::Memset(currentStack.start, 0xFF, nt.stackSize);
|
||||
context.r[MIPS_REG_SP] = currentStack.start + nt.stackSize;
|
||||
currentStack.end = context.r[MIPS_REG_SP];
|
||||
// The k0 section is 256 bytes at the top of the stack.
|
||||
context.r[MIPS_REG_SP] -= 256;
|
||||
context.r[MIPS_REG_K0] = context.r[MIPS_REG_SP];
|
||||
|
@ -345,19 +345,51 @@ public:
|
|||
}
|
||||
|
||||
void FreeStack() {
|
||||
if (stackBlock != 0) {
|
||||
if (currentStack.start != 0) {
|
||||
DEBUG_LOG(HLE, "Freeing thread stack %s", nt.name);
|
||||
if (nt.attr & PSP_THREAD_ATTR_KERNEL) {
|
||||
kernelMemory.Free(stackBlock);
|
||||
kernelMemory.Free(currentStack.start);
|
||||
} else {
|
||||
userMemory.Free(stackBlock);
|
||||
userMemory.Free(currentStack.start);
|
||||
}
|
||||
stackBlock = 0;
|
||||
currentStack.start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Thread() : stackBlock(0)
|
||||
bool PushExtendedStack(u32 size)
|
||||
{
|
||||
u32 stack = userMemory.Alloc(size, true, (std::string("extended/") + nt.name).c_str());
|
||||
if (stack == (u32)-1)
|
||||
return false;
|
||||
|
||||
pushedStacks.push_back(currentStack);
|
||||
currentStack.start = stack;
|
||||
currentStack.end = stack + size;
|
||||
nt.initialStack = currentStack.start;
|
||||
nt.stackSize = currentStack.end - currentStack.start;
|
||||
|
||||
// We still drop the threadID at the bottom and fill it, but there's no k0.
|
||||
Memory::Memset(currentStack.start, 0xFF, nt.stackSize);
|
||||
Memory::Write_U32(GetUID(), nt.initialStack);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PopExtendedStack()
|
||||
{
|
||||
if (pushedStacks.size() == 0)
|
||||
return false;
|
||||
|
||||
userMemory.Free(currentStack.start);
|
||||
currentStack = pushedStacks.back();
|
||||
pushedStacks.pop_back();
|
||||
nt.initialStack = currentStack.start;
|
||||
nt.stackSize = currentStack.end - currentStack.start;
|
||||
return true;
|
||||
}
|
||||
|
||||
Thread()
|
||||
{
|
||||
currentStack.start = 0;
|
||||
}
|
||||
|
||||
~Thread()
|
||||
|
@ -406,7 +438,8 @@ public:
|
|||
}
|
||||
|
||||
p.Do(pendingMipsCalls);
|
||||
p.Do(stackBlock);
|
||||
p.Do(pushedStacks);
|
||||
p.Do(currentStack);
|
||||
|
||||
p.DoMarker("Thread");
|
||||
}
|
||||
|
@ -427,8 +460,15 @@ public:
|
|||
|
||||
std::list<u32> pendingMipsCalls;
|
||||
|
||||
u32 stackBlock;
|
||||
u32 stackEnd;
|
||||
struct StackInfo {
|
||||
u32 start;
|
||||
u32 end;
|
||||
};
|
||||
// This is a stack of... stacks, since sceKernelExtendThreadStack() can recurse.
|
||||
// These are stacks that aren't "active" right now, but will pop off once the func returns.
|
||||
std::vector<StackInfo> pushedStacks;
|
||||
|
||||
StackInfo currentStack;
|
||||
};
|
||||
|
||||
struct ThreadQueueList
|
||||
|
@ -700,6 +740,7 @@ u32 idleThreadHackAddr;
|
|||
u32 threadReturnHackAddr;
|
||||
u32 cbReturnHackAddr;
|
||||
u32 intReturnHackAddr;
|
||||
u32 extendReturnHackAddr;
|
||||
std::vector<ThreadCallback> threadEndListeners;
|
||||
|
||||
// Lists all thread ids that aren't deleted/etc.
|
||||
|
@ -801,9 +842,25 @@ u32 __KernelInterruptReturnAddress()
|
|||
void hleScheduledWakeup(u64 userdata, int cyclesLate);
|
||||
void hleThreadEndTimeout(u64 userdata, int cyclesLate);
|
||||
|
||||
void __KernelWriteFakeSysCall(u32 nid, u32 &ptr, u32 &pos)
|
||||
{
|
||||
ptr = pos;
|
||||
pos += 8;
|
||||
WriteSyscall("FakeSysCalls", nid, ptr);
|
||||
}
|
||||
|
||||
void __KernelThreadingInit()
|
||||
{
|
||||
u32 blockSize = 4 * 4 + 4 * 2 * 3; // One 16-byte thread plus 3 8-byte "hacks"
|
||||
// Yeah, this is straight out of JPCSP, I should be ashamed.
|
||||
const static u32 idleThreadCode[] = {
|
||||
MIPS_MAKE_ADDIU(MIPS_REG_A0, MIPS_REG_ZERO, 0),
|
||||
MIPS_MAKE_LUI(MIPS_REG_RA, 0x0800),
|
||||
MIPS_MAKE_JR_RA(),
|
||||
//MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelDelayThread"),
|
||||
MIPS_MAKE_SYSCALL("FakeSysCalls", "_sceKernelIdle"),
|
||||
MIPS_MAKE_BREAK(),
|
||||
};
|
||||
u32 blockSize = sizeof(idleThreadCode) + 4 * 2 * 4; // The thread code above plus 4 8-byte "hacks"
|
||||
|
||||
dispatchEnabled = true;
|
||||
memset(waitTypeFuncs, 0, sizeof(waitTypeFuncs));
|
||||
|
@ -813,25 +870,15 @@ void __KernelThreadingInit()
|
|||
currentCallbackThreadID = 0;
|
||||
readyCallbacksCount = 0;
|
||||
idleThreadHackAddr = kernelMemory.Alloc(blockSize, false, "threadrethack");
|
||||
// Make sure it got allocated where we expect it... at the very start of kernel RAM
|
||||
//CHECK_EQ(idleThreadHackAddr & 0x3FFFFFFF, 0x08000000);
|
||||
|
||||
// Yeah, this is straight out of JPCSP, I should be ashamed.
|
||||
Memory::Write_U32(MIPS_MAKE_ADDIU(MIPS_REG_A0, MIPS_REG_ZERO, 0), idleThreadHackAddr);
|
||||
Memory::Write_U32(MIPS_MAKE_LUI(MIPS_REG_RA, 0x0800), idleThreadHackAddr + 4);
|
||||
Memory::Write_U32(MIPS_MAKE_JR_RA(), idleThreadHackAddr + 8);
|
||||
//Memory::Write_U32(MIPS_MAKE_SYSCALL("ThreadManForUser", "sceKernelDelayThread"), idleThreadHackAddr + 12);
|
||||
Memory::Write_U32(MIPS_MAKE_SYSCALL("FakeSysCalls", "_sceKernelIdle"), idleThreadHackAddr + 12);
|
||||
Memory::Write_U32(MIPS_MAKE_BREAK(), idleThreadHackAddr + 16);
|
||||
Memory::Memcpy(idleThreadHackAddr, idleThreadCode, sizeof(idleThreadCode));
|
||||
|
||||
threadReturnHackAddr = idleThreadHackAddr + 20;
|
||||
WriteSyscall("FakeSysCalls", NID_THREADRETURN, threadReturnHackAddr);
|
||||
|
||||
cbReturnHackAddr = threadReturnHackAddr + 8;
|
||||
WriteSyscall("FakeSysCalls", NID_CALLBACKRETURN, cbReturnHackAddr);
|
||||
|
||||
intReturnHackAddr = cbReturnHackAddr + 8;
|
||||
WriteSyscall("FakeSysCalls", NID_INTERRUPTRETURN, intReturnHackAddr);
|
||||
u32 pos = idleThreadHackAddr + sizeof(idleThreadCode);
|
||||
// IF you add another func here, add it to the allocation above, and also to DoState below.
|
||||
__KernelWriteFakeSysCall(NID_THREADRETURN, threadReturnHackAddr, pos);
|
||||
__KernelWriteFakeSysCall(NID_CALLBACKRETURN, cbReturnHackAddr, pos);
|
||||
__KernelWriteFakeSysCall(NID_INTERRUPTRETURN, intReturnHackAddr, pos);
|
||||
__KernelWriteFakeSysCall(NID_EXTENDRETURN, extendReturnHackAddr, pos);
|
||||
|
||||
eventScheduledWakeup = CoreTiming::RegisterEvent("ScheduledWakeup", &hleScheduledWakeup);
|
||||
eventThreadEndTimeout = CoreTiming::RegisterEvent("ThreadEndTimeout", &hleThreadEndTimeout);
|
||||
|
@ -857,6 +904,7 @@ void __KernelThreadingDoState(PointerWrap &p)
|
|||
p.Do(threadReturnHackAddr);
|
||||
p.Do(cbReturnHackAddr);
|
||||
p.Do(intReturnHackAddr);
|
||||
p.Do(extendReturnHackAddr);
|
||||
|
||||
p.Do(currentThread);
|
||||
SceUID dv = 0;
|
||||
|
@ -1611,7 +1659,7 @@ int sceKernelCheckThreadStack()
|
|||
u32 error;
|
||||
Thread *t = kernelObjects.Get<Thread>(__KernelGetCurThread(), error);
|
||||
if (t) {
|
||||
u32 diff = labs((long)((s64)t->stackEnd - (s64)currentMIPS->r[MIPS_REG_SP]));
|
||||
u32 diff = labs((long)((s64)t->currentStack.end - (s64)currentMIPS->r[MIPS_REG_SP]));
|
||||
WARN_LOG(HLE, "%i=sceKernelCheckThreadStack()", diff);
|
||||
return diff;
|
||||
} else {
|
||||
|
@ -1726,7 +1774,7 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr
|
|||
//grab mips regs
|
||||
SceUID id;
|
||||
Thread *thread = __KernelCreateThread(id, moduleID, "root", currentMIPS->pc, prio, stacksize, attr);
|
||||
if (thread->stackBlock == 0)
|
||||
if (thread->currentStack.start == 0)
|
||||
ERROR_LOG_REPORT(HLE, "Unable to allocate stack for root thread.");
|
||||
__KernelResetThread(thread);
|
||||
|
||||
|
@ -1781,7 +1829,7 @@ int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32
|
|||
|
||||
SceUID id;
|
||||
Thread *newThread = __KernelCreateThread(id, moduleID, threadName, entry, prio, stacksize, attr);
|
||||
if (newThread->stackBlock == 0)
|
||||
if (newThread->currentStack.start == 0)
|
||||
{
|
||||
ERROR_LOG_REPORT(HLE, "sceKernelCreateThread(name=%s): out of memory, %08x stack requested", threadName, stacksize);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
|
@ -1900,7 +1948,7 @@ void sceKernelGetThreadStackFreeSize()
|
|||
|
||||
// Scan the stack for 0xFF
|
||||
int sz = 0;
|
||||
for (u32 addr = thread->stackBlock; addr < thread->stackBlock + thread->nt.stackSize; addr++)
|
||||
for (u32 addr = thread->currentStack.start; addr < thread->currentStack.start + thread->nt.stackSize; addr++)
|
||||
{
|
||||
if (Memory::Read_U8(addr) != 0xFF)
|
||||
break;
|
||||
|
@ -2140,7 +2188,7 @@ u32 __KernelGetCurThreadStack()
|
|||
{
|
||||
Thread *t = __GetCurrentThread();
|
||||
if (t)
|
||||
return t->stackEnd;
|
||||
return t->currentStack.end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2557,12 +2605,70 @@ int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr)
|
|||
|
||||
u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter)
|
||||
{
|
||||
ERROR_LOG_REPORT(HLE,"sceKernelExtendThreadStack(%08x, %08x, %08x) - Not fully supported", size, entryAddr, entryParameter);
|
||||
u32 args[1] = { entryParameter };
|
||||
__KernelDirectMipsCall(entryAddr, 0, args, 1, false);
|
||||
if (size < 512)
|
||||
{
|
||||
ERROR_LOG_REPORT(HLE, "sceKernelExtendThreadStack(%08x, %08x, %08x) - stack size too small", size, entryAddr, entryParameter);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_STACK_SIZE;
|
||||
}
|
||||
|
||||
Thread *thread = __GetCurrentThread();
|
||||
if (!thread)
|
||||
{
|
||||
ERROR_LOG_REPORT(HLE, "sceKernelExtendThreadStack(%08x, %08x, %08x) - not on a thread?", size, entryAddr, entryParameter);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!thread->PushExtendedStack(size))
|
||||
{
|
||||
ERROR_LOG_REPORT(HLE, "sceKernelExtendThreadStack(%08x, %08x, %08x) - could not allocate new stack", size, entryAddr, entryParameter);
|
||||
return SCE_KERNEL_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
// The stack has been changed now, so it's do or die time.
|
||||
DEBUG_LOG(HLE, "sceKernelExtendThreadStack(%08x, %08x, %08x)", size, entryAddr, entryParameter);
|
||||
|
||||
// Push the old SP, RA, and PC onto the stack (so we can restore them later.)
|
||||
Memory::Write_U32(currentMIPS->r[MIPS_REG_RA], thread->currentStack.end - 4);
|
||||
Memory::Write_U32(currentMIPS->r[MIPS_REG_SP], thread->currentStack.end - 8);
|
||||
Memory::Write_U32(currentMIPS->pc, thread->currentStack.end - 12);
|
||||
|
||||
currentMIPS->pc = entryAddr;
|
||||
currentMIPS->r[MIPS_REG_A0] = entryParameter;
|
||||
currentMIPS->r[MIPS_REG_RA] = extendReturnHackAddr;
|
||||
// Stack should stay aligned even though we saved only 3 regs.
|
||||
currentMIPS->r[MIPS_REG_SP] = thread->currentStack.end - 0x10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __KernelReturnFromExtendStack()
|
||||
{
|
||||
Thread *thread = __GetCurrentThread();
|
||||
if (!thread)
|
||||
{
|
||||
ERROR_LOG_REPORT(HLE, "__KernelReturnFromExtendStack() - not on a thread?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the saved regs at the top of the stack.
|
||||
u32 restoreRA = Memory::Read_U32(thread->currentStack.end - 4);
|
||||
u32 restoreSP = Memory::Read_U32(thread->currentStack.end - 8);
|
||||
u32 restorePC = Memory::Read_U32(thread->currentStack.end - 12);
|
||||
|
||||
if (!thread->PopExtendedStack())
|
||||
{
|
||||
ERROR_LOG_REPORT(HLE, "__KernelReturnFromExtendStack() - no stack to restore?");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(HLE, "__KernelReturnFromExtendStack()");
|
||||
currentMIPS->r[MIPS_REG_RA] = restoreRA;
|
||||
currentMIPS->r[MIPS_REG_SP] = restoreSP;
|
||||
currentMIPS->pc = restorePC;
|
||||
|
||||
// We retain whatever is in v0/v1, it gets passed on to the caller of sceKernelExtendThreadStack().
|
||||
}
|
||||
|
||||
void ActionAfterMipsCall::run(MipsCall &call) {
|
||||
u32 error;
|
||||
Thread *thread = kernelObjects.Get<Thread>(threadID, error);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue