Merge pull request #1935 from unknownbrackets/modules

Get sceKernelStopModule() sorta working
This commit is contained in:
Henrik Rydgård 2013-05-26 11:52:01 -07:00
commit 315559afe6
7 changed files with 214 additions and 40 deletions

View File

@ -79,6 +79,7 @@ const HLEFunction FakeSysCalls[] = {
{NID_CALLBACKRETURN, __KernelReturnFromMipsCall, "__KernelReturnFromMipsCall"},
{NID_INTERRUPTRETURN, __KernelReturnFromInterrupt, "__KernelReturnFromInterrupt"},
{NID_EXTENDRETURN, __KernelReturnFromExtendStack, "__KernelReturnFromExtendStack"},
{NID_MODULERETURN, __KernelReturnFromModuleFunc, "__KernelReturnFromModuleFunc"},
{NID_IDLE, __KernelIdle, "_sceKernelIdle"},
};

View File

@ -23,6 +23,7 @@
#define NID_CALLBACKRETURN 0xbadc0fee
#define NID_INTERRUPTRETURN 0xbadd00d5
#define NID_EXTENDRETURN 0xbad0b0c9
#define NID_MODULERETURN 0xbad0d318
#define NID_IDLE 0x1d7e1d7e
void RegisterAllModules();

View File

@ -687,7 +687,7 @@ const HLEFunction ThreadManForUser[] =
{0x809ce29b,WrapV_I<sceKernelExitDeleteThread>,"sceKernelExitDeleteThread"},
{0x94aa61ee,sceKernelGetThreadCurrentPriority,"sceKernelGetThreadCurrentPriority"},
{0x293b45b8,WrapI_V<sceKernelGetThreadId>,"sceKernelGetThreadId"},
{0x3B183E26,sceKernelGetThreadExitStatus,"sceKernelGetThreadExitStatus"},
{0x3B183E26,WrapI_I<sceKernelGetThreadExitStatus>,"sceKernelGetThreadExitStatus"},
{0x52089CA1,sceKernelGetThreadStackFreeSize,"sceKernelGetThreadStackFreeSize"},
{0xFFC36A14,WrapU_UU<sceKernelReferThreadRunStatus>,"sceKernelReferThreadRunStatus"},
{0x17c1684e,WrapU_UU<sceKernelReferThreadStatus>,"sceKernelReferThreadStatus"},

View File

@ -19,7 +19,8 @@
#include <algorithm>
#include <string>
#include "HLE.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/HLETables.h"
#include "Core/Reporting.h"
#include "Common/FileUtil.h"
#include "../Host.h"
@ -161,11 +162,16 @@ struct ModuleInfo {
char name[28];
};
struct ModuleWaitingThread
{
SceUID threadID;
u32 statusPtr;
};
class Module : public KernelObject
{
public:
Module() : memoryBlockAddr(0), isFake(false) {}
Module() : memoryBlockAddr(0), isFake(false), isStarted(false) {}
~Module() {
if (memoryBlockAddr) {
userMemory.Free(memoryBlockAddr);
@ -190,14 +196,21 @@ public:
p.Do(nm);
p.Do(memoryBlockAddr);
p.Do(memoryBlockSize);
p.Do(isFake);
p.Do(isStarted);
ModuleWaitingThread mwt = {0};
p.Do(waitingThreads, mwt);
p.DoMarker("Module");
}
NativeModule nm;
std::vector<ModuleWaitingThread> waitingThreads;
u32 memoryBlockAddr;
u32 memoryBlockSize;
bool isFake;
// Probably one of the NativeModule fields, but not sure...
bool isStarted;
};
KernelObject *__KernelModuleObject()
@ -863,14 +876,15 @@ Module *__KernelLoadModule(u8 *fileptr, SceKernelLMOption *options, std::string
void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOption *options)
{
m->isStarted = true;
if (m->nm.module_start_func != 0 && m->nm.module_start_func != (u32)-1)
{
if (m->nm.module_start_func != m->nm.entry_addr)
WARN_LOG_REPORT(LOADER, "Main module has start func (%08x) different from entry (%08x)?", m->nm.module_start_func, m->nm.entry_addr);
}
__KernelSetupRootThread(m->GetUID(), args, argp, options->priority, options->stacksize, options->attribute);
//TODO: if current thread, put it in wait state, waiting for the new thread
SceUID threadID = __KernelSetupRootThread(m->GetUID(), args, argp, options->priority, options->stacksize, options->attribute);
__KernelSetThreadRA(threadID, NID_MODULERETURN);
}
@ -1053,7 +1067,7 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
int stackPartition = 0;
SceKernelSMOption smoption;
if (optionAddr) {
Memory::ReadStruct(optionAddr, &smoption);;
Memory::ReadStruct(optionAddr, &smoption);
}
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
@ -1063,8 +1077,17 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
} else if (module->isFake) {
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): faked (undecryptable module)",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
if (returnValueAddr)
Memory::Write_U32(0, returnValueAddr);
RETURN(moduleId);
return;
} else if (module->isStarted) {
ERROR_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x) : already started",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
// TODO: Maybe should be SCE_KERNEL_ERROR_ALREADY_STARTED, but I get SCE_KERNEL_ERROR_ERROR.
// But I also get crashes...
RETURN(SCE_KERNEL_ERROR_ERROR);
return;
} else {
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
@ -1085,7 +1108,8 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
}
else
{
// TODO: Fix, check return value? Or do we call nothing?
// TODO: Why are we just returning the module ID in this case?
ERROR_LOG_REPORT(HLE, "sceKernelStartModule(): doing nothing for some reason?");
RETURN(moduleId);
return;
}
@ -1106,39 +1130,104 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
}
SceUID threadID = __KernelCreateThread(module->nm.name, moduleId, entryAddr, priority, stacksize, attribute, 0);
sceKernelStartThread(threadID, argsize, argAddr);
// TODO: This will probably return the wrong value?
sceKernelWaitThreadEnd(threadID, 0);
__KernelSetThreadRA(threadID, NID_MODULERETURN);
__KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "started module");
const ModuleWaitingThread mwt = {__KernelGetCurThread(), returnValueAddr};
module->waitingThreads.push_back(mwt);
}
else if (entryAddr == 0)
{
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)",
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): no entry address",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
WARN_LOG(HLE, "No Entry Address");
}
else
{
ERROR_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)",
ERROR_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): invalid entry address",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
ERROR_LOG(HLE, "Invalid Entry Address");
RETURN(-1);
return;
}
}
// TODO: Is this the correct return value?
// JPCSP returns this value as well.
RETURN(moduleId);
}
u32 sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 returnValueAddr, u32 optionAddr)
{
ERROR_LOG(HLE,"UNIMPL sceKernelStopModule(%08x, %08x, %08x, %08x, %08x)", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
u32 priority = 0x20;
u32 stacksize = 0x40000;
u32 attr = 0;
// TODO: In a lot of cases (even for errors), this should resched. Needs testing.
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
if (!module)
{
ERROR_LOG(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): invalid module id", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
return error;
}
if (module->isFake)
{
INFO_LOG(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x) - faking", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
if (returnValueAddr)
Memory::Write_U32(0, returnValueAddr);
return 0;
}
if (!module->isStarted)
{
ERROR_LOG(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): already stopped", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
return SCE_KERNEL_ERROR_ALREADY_STOPPED;
}
u32 stopFunc = module->nm.module_stop_func;
if (module->nm.module_stop_thread_priority != 0)
priority = module->nm.module_stop_thread_priority;
if (module->nm.module_stop_thread_stacksize != 0)
stacksize = module->nm.module_stop_thread_stacksize;
if (module->nm.module_stop_thread_attr != 0)
attr = module->nm.module_stop_thread_attr;
// TODO: Need to test how this really works. Let's assume it's an override.
if (Memory::IsValidAddress(optionAddr))
{
auto options = Memory::GetStruct<SceKernelSMOption>(optionAddr);
// TODO: Check how size handling actually works.
if (options->size != 0 && options->priority != 0)
priority = options->priority;
if (options->size != 0 && options->stacksize != 0)
stacksize = options->stacksize;
if (options->size != 0 && options->attribute != 0)
attr = options->attribute;
// TODO: Maybe based on size?
else if (attr != 0)
WARN_LOG_REPORT(HLE, "Stopping module with attr=%x, but options specify 0", attr);
}
if (Memory::IsValidAddress(stopFunc))
{
SceUID threadID = __KernelCreateThread(module->nm.name, moduleId, stopFunc, priority, stacksize, attr, 0);
sceKernelStartThread(threadID, argSize, argAddr);
__KernelSetThreadRA(threadID, NID_MODULERETURN);
__KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "stopped module");
const ModuleWaitingThread mwt = {__KernelGetCurThread(), returnValueAddr};
module->waitingThreads.push_back(mwt);
}
else if (stopFunc == 0)
{
INFO_LOG(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): no stop func, skipping", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
module->isStarted = false;
}
else
{
ERROR_LOG_REPORT(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): bad stop func address", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
module->isStarted = false;
}
// We should call the "stop" entry point and return the value in returnValueAddr. See StartModule.
// Possibly also kill all its threads?
return 0;
}
@ -1162,6 +1251,43 @@ u32 sceKernelStopUnloadSelfModuleWithStatus(u32 exitCode, u32 argSize, u32 argp,
return 0;
}
void __KernelReturnFromModuleFunc()
{
// Return from the thread as normal.
__KernelReturnFromThread();
SceUID leftModuleID = __KernelGetCurThreadModuleId();
SceUID leftThreadID = __KernelGetCurThread();
int exitStatus = sceKernelGetThreadExitStatus(leftThreadID);
// Reschedule immediately (to leave the thread) and delete it and its stack.
__KernelReSchedule("returned from module");
sceKernelDeleteThread(leftThreadID);
u32 error;
Module *module = kernelObjects.Get<Module>(leftModuleID, error);
if (!module)
{
ERROR_LOG_REPORT(HLE, "Returned from deleted module start/stop func");
return;
}
// We can't be starting and stopping at the same time, so no need to differentiate.
module->isStarted = !module->isStarted;
for (auto it = module->waitingThreads.begin(), end = module->waitingThreads.end(); it < end; ++it)
{
// Still waiting?
SceUID waitingModuleID = __KernelGetWaitID(it->threadID, WAITTYPE_MODULE, error);
if (waitingModuleID == leftModuleID)
{
if (it->statusPtr != 0)
Memory::Write_U32(exitStatus, it->statusPtr);
__KernelResumeThreadFromWait(it->threadID, 0);
}
}
module->waitingThreads.clear();
}
struct GetModuleIdByAddressArg
{
u32 addr;

View File

@ -26,5 +26,6 @@ void __KernelModuleShutdown();
u32 __KernelGetModuleGP(SceUID module);
bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std::string *error_string);
void __KernelReturnFromModuleFunc();
void Register_ModuleMgrForUser();

View File

@ -746,6 +746,7 @@ u32 threadReturnHackAddr;
u32 cbReturnHackAddr;
u32 intReturnHackAddr;
u32 extendReturnHackAddr;
u32 moduleReturnHackAddr;
std::vector<ThreadCallback> threadEndListeners;
// Lists all thread ids that aren't deleted/etc.
@ -844,18 +845,52 @@ u32 __KernelInterruptReturnAddress()
return intReturnHackAddr;
}
u32 __KernelSetThreadRA(SceUID threadID, int nid)
{
u32 newRA;
switch (nid)
{
case NID_MODULERETURN:
newRA = moduleReturnHackAddr;
break;
default:
ERROR_LOG_REPORT(HLE, "__KernelSetThreadRA(): invalid RA address");
return -1;
}
if (threadID == currentThread)
currentMIPS->r[MIPS_REG_RA] = newRA;
else
{
u32 error;
Thread *thread = kernelObjects.Get<Thread>(threadID, error);
if (!thread)
return error;
thread->context.r[MIPS_REG_RA] = newRA;
}
return 0;
}
void hleScheduledWakeup(u64 userdata, int cyclesLate);
void hleThreadEndTimeout(u64 userdata, int cyclesLate);
void __KernelWriteFakeSysCall(u32 nid, u32 &ptr, u32 &pos)
void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos)
{
ptr = pos;
*ptr = pos;
pos += 8;
WriteSyscall("FakeSysCalls", nid, ptr);
WriteSyscall("FakeSysCalls", nid, *ptr);
}
void __KernelThreadingInit()
{
struct ThreadHack
{
u32 nid;
u32 *addr;
};
// 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),
@ -865,7 +900,16 @@ void __KernelThreadingInit()
MIPS_MAKE_SYSCALL("FakeSysCalls", "_sceKernelIdle"),
MIPS_MAKE_BREAK(),
};
u32 blockSize = sizeof(idleThreadCode) + 4 * 2 * 4; // The thread code above plus 4 8-byte "hacks"
// If you add another func here, don't forget __KernelThreadingDoState() below.
static ThreadHack threadHacks[] = {
{NID_THREADRETURN, &threadReturnHackAddr},
{NID_CALLBACKRETURN, &cbReturnHackAddr},
{NID_INTERRUPTRETURN, &intReturnHackAddr},
{NID_EXTENDRETURN, &extendReturnHackAddr},
{NID_MODULERETURN, &moduleReturnHackAddr},
};
u32 blockSize = sizeof(idleThreadCode) + ARRAY_SIZE(threadHacks) * 2 * 4; // The thread code above plus 8 bytes per "hack"
dispatchEnabled = true;
memset(waitTypeFuncs, 0, sizeof(waitTypeFuncs));
@ -879,11 +923,9 @@ void __KernelThreadingInit()
Memory::Memcpy(idleThreadHackAddr, idleThreadCode, sizeof(idleThreadCode));
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);
for (int i = 0; i < ARRAY_SIZE(threadHacks); ++i) {
__KernelWriteFakeSysCall(threadHacks[i].nid, threadHacks[i].addr, pos);
}
eventScheduledWakeup = CoreTiming::RegisterEvent("ScheduledWakeup", &hleScheduledWakeup);
eventThreadEndTimeout = CoreTiming::RegisterEvent("ThreadEndTimeout", &hleThreadEndTimeout);
@ -910,6 +952,7 @@ void __KernelThreadingDoState(PointerWrap &p)
p.Do(cbReturnHackAddr);
p.Do(intReturnHackAddr);
p.Do(extendReturnHackAddr);
p.Do(moduleReturnHackAddr);
p.Do(currentThread);
SceUID dv = 0;
@ -1256,9 +1299,8 @@ u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr)
return 0;
}
void sceKernelGetThreadExitStatus()
int sceKernelGetThreadExitStatus(SceUID threadID)
{
SceUID threadID = PARAM(0);
if (threadID == 0)
threadID = __KernelGetCurThread();
@ -1269,17 +1311,17 @@ void sceKernelGetThreadExitStatus()
if (t->nt.status == THREADSTATUS_DORMANT) // TODO: can be dormant before starting, too, need to avoid that
{
DEBUG_LOG(HLE,"sceKernelGetThreadExitStatus(%i)", threadID);
RETURN(t->nt.exitStatus);
return t->nt.exitStatus;
}
else
{
RETURN(SCE_KERNEL_ERROR_NOT_DORMANT);
return SCE_KERNEL_ERROR_NOT_DORMANT;
}
}
else
{
ERROR_LOG(HLE,"sceKernelGetThreadExitStatus Error %08x", error);
RETURN(SCE_KERNEL_ERROR_UNKNOWN_THID);
return SCE_KERNEL_ERROR_UNKNOWN_THID;
}
}
@ -1770,7 +1812,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32
return t;
}
void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int prio, int stacksize, int attr)
SceUID __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int prio, int stacksize, int attr)
{
//grab mips regs
SceUID id;
@ -1795,6 +1837,8 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr
mipsr4k.r[MIPS_REG_A1] = location;
for (int i = 0; i < args; i++)
Memory::Write_U8(argp[i], location + i);
return id;
}
int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr)
@ -1966,10 +2010,6 @@ void __KernelReturnFromThread()
_dbg_assert_msg_(HLE, thread != NULL, "Returned from a NULL thread.");
INFO_LOG(HLE,"__KernelReturnFromThread: %d", exitStatus);
// TEMPORARY HACK: kill the stack of the root thread early:
if (!strcmp(thread->GetName(), "root")) {
thread->FreeStack();
}
thread->nt.exitStatus = exitStatus;
__KernelChangeReadyState(thread, currentThread, false);

View File

@ -56,7 +56,7 @@ int sceKernelSleepThreadCB();
int sceKernelTerminateDeleteThread(int threadno);
int sceKernelTerminateThread(SceUID threadID);
int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr);
void sceKernelGetThreadExitStatus();
int sceKernelGetThreadExitStatus(SceUID threadID);
u32 sceKernelGetThreadmanIdType(u32);
u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 idCountPtr);
u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter);
@ -89,6 +89,7 @@ enum WaitType
WAITTYPE_IO = 16,
WAITTYPE_GEDRAWSYNC = 17,
WAITTYPE_GELISTSYNC = 18,
WAITTYPE_MODULE = 19,
NUM_WAITTYPES
};
@ -186,7 +187,7 @@ u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int not
SceUID __KernelGetCurThread();
SceUID __KernelGetCurThreadModuleId();
void __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution
SceUID __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution
void __KernelStartIdleThreads(SceUID moduleId);
void __KernelReturnFromThread(); // Called as HLE function
u32 __KernelGetThreadPrio(SceUID id);
@ -232,6 +233,10 @@ void __KernelNotifyCallback(RegisteredCallbackType type, SceUID cbId, int notify
bool __KernelSwitchOffThread(const char *reason);
bool __KernelSwitchToThread(SceUID threadID, const char *reason);
// Set a thread's return address to a specific FakeSyscall nid.
// Discards old RA. Only useful for special threads that do special things on exit.
u32 __KernelSetThreadRA(SceUID threadID, int nid);
// A call into game code. These can be pending on a thread.
// Similar to Callback-s (NOT CallbackInfos) in JPCSP.
typedef Action *(*ActionCreator)();