Merge pull request #5092 from unknownbrackets/module-minor

Try to implement the unload/stop self module func
This commit is contained in:
Henrik Rydgård 2014-01-12 13:56:19 -08:00
commit 108d592461

View File

@ -194,16 +194,22 @@ struct ModuleInfo {
char name[28];
};
struct ModuleWaitingThread
{
struct ModuleWaitingThread {
SceUID threadID;
u32 statusPtr;
};
class Module : public KernelObject
{
enum NativeModuleStatus {
MODULE_STATUS_STARTING = 4,
MODULE_STATUS_STARTED = 5,
MODULE_STATUS_STOPPING = 6,
MODULE_STATUS_STOPPED = 7,
MODULE_STATUS_UNLOADING = 8,
};
class Module : public KernelObject {
public:
Module() : memoryBlockAddr(0), isFake(false), isStarted(false) {}
Module() : memoryBlockAddr(0), isFake(false) {}
~Module() {
if (memoryBlockAddr) {
userMemory.Free(memoryBlockAddr);
@ -226,7 +232,7 @@ public:
virtual void DoState(PointerWrap &p)
{
auto s = p.Section("Module", 1);
auto s = p.Section("Module", 1, 2);
if (!s)
return;
@ -234,7 +240,16 @@ public:
p.Do(memoryBlockAddr);
p.Do(memoryBlockSize);
p.Do(isFake);
p.Do(isStarted);
if (s < 2) {
bool isStarted = false;
p.Do(isStarted);
if (isStarted)
nm.status = MODULE_STATUS_STARTED;
else
nm.status = MODULE_STATUS_STOPPED;
}
ModuleWaitingThread mwt = {0};
p.Do(waitingThreads, mwt);
FuncSymbolExport fsx = {{0}};
@ -325,8 +340,6 @@ public:
u32 memoryBlockAddr;
u32 memoryBlockSize;
bool isFake;
// Probably one of the NativeModule fields, but not sure...
bool isStarted;
};
KernelObject *__KernelModuleObject()
@ -1218,7 +1231,7 @@ Module *__KernelLoadModule(u8 *fileptr, SceKernelLMOption *options, std::string
void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOption *options)
{
m->isStarted = true;
m->nm.status = MODULE_STATUS_STARTED;
if (m->nm.module_start_func != 0 && m->nm.module_start_func != (u32)-1)
{
if (m->nm.module_start_func != m->nm.entry_addr)
@ -1493,7 +1506,7 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
Memory::Write_U32(0, returnValueAddr);
RETURN(moduleId);
return;
} else if (module->isStarted) {
} else if (module->nm.status == MODULE_STATUS_STARTED) {
ERROR_LOG(SCEMODULE, "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.
@ -1523,7 +1536,7 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
{
// TODO: Why are we just returning the module ID in this case?
WARN_LOG(SCEMODULE, "sceKernelStartModule(): module has no start or entry func");
module->isStarted = true;
module->nm.status = MODULE_STATUS_STARTED;
RETURN(moduleId);
return;
}
@ -1549,13 +1562,14 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
__KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "started module");
const ModuleWaitingThread mwt = {__KernelGetCurThread(), returnValueAddr};
module->nm.status = MODULE_STATUS_STARTING;
module->waitingThreads.push_back(mwt);
}
else if (entryAddr == 0)
{
INFO_LOG(SCEMODULE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): no entry address",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
module->isStarted = true;
module->nm.status = MODULE_STATUS_STARTED;
}
else
{
@ -1592,7 +1606,7 @@ u32 sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 returnValueA
Memory::Write_U32(0, returnValueAddr);
return 0;
}
if (!module->isStarted)
if (module->nm.status != MODULE_STATUS_STARTED)
{
ERROR_LOG(SCEMODULE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): already stopped", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
return SCE_KERNEL_ERROR_ALREADY_STOPPED;
@ -1630,17 +1644,18 @@ u32 sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 returnValueA
__KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "stopped module");
const ModuleWaitingThread mwt = {__KernelGetCurThread(), returnValueAddr};
module->nm.status = MODULE_STATUS_STOPPING;
module->waitingThreads.push_back(mwt);
}
else if (stopFunc == 0)
{
INFO_LOG(SCEMODULE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): no stop func, skipping", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
module->isStarted = false;
module->nm.status = MODULE_STATUS_STOPPED;
}
else
{
ERROR_LOG_REPORT(SCEMODULE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): bad stop func address", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
module->isStarted = false;
module->nm.status = MODULE_STATUS_STOPPED;
}
return 0;
@ -1659,9 +1674,70 @@ u32 sceKernelUnloadModule(u32 moduleId)
return moduleId;
}
u32 sceKernelStopUnloadSelfModuleWithStatus(u32 exitCode, u32 argSize, u32 argp, u32 statusAddr, u32 optionAddr)
{
ERROR_LOG_REPORT(SCEMODULE, "UNIMPL sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x): game has likely crashed", exitCode, argSize, argp, statusAddr, optionAddr);
u32 sceKernelStopUnloadSelfModuleWithStatus(u32 exitCode, u32 argSize, u32 argp, u32 statusAddr, u32 optionAddr) {
if (loadedModules.size() > 1) {
ERROR_LOG_REPORT(SCEMODULE, "UNIMPL sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x): game may have crashed", exitCode, argSize, argp, statusAddr, optionAddr);
SceUID moduleID = __KernelGetCurThreadModuleId();
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(SCEMODULE, "sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x): invalid module id", exitCode, argSize, argp, statusAddr, optionAddr);
return error;
}
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(SCEMODULE, "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, argp);
__KernelSetThreadRA(threadID, NID_MODULERETURN);
__KernelWaitCurThread(WAITTYPE_MODULE, moduleID, 1, 0, false, "unloadstopped module");
const ModuleWaitingThread mwt = {__KernelGetCurThread(), statusAddr};
module->nm.status = MODULE_STATUS_UNLOADING;
module->waitingThreads.push_back(mwt);
} else if (stopFunc == 0) {
INFO_LOG(SCEMODULE, "sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x): no stop func", exitCode, argSize, argp, statusAddr, optionAddr);
sceKernelExitDeleteThread(exitCode);
module->Cleanup();
kernelObjects.Destroy<Module>(moduleID);
} else {
ERROR_LOG_REPORT(SCEMODULE, "sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x): bad stop func address", exitCode, argSize, argp, statusAddr, optionAddr);
sceKernelExitDeleteThread(exitCode);
module->Cleanup();
kernelObjects.Destroy<Module>(moduleID);
}
} else {
ERROR_LOG_REPORT(SCEMODULE, "UNIMPL sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x): game has likely crashed", exitCode, argSize, argp, statusAddr, optionAddr);
}
// Probably similar to sceKernelStopModule, but games generally call this when they die.
return 0;
@ -1683,25 +1759,36 @@ void __KernelReturnFromModuleFunc()
u32 error;
Module *module = kernelObjects.Get<Module>(leftModuleID, error);
if (!module)
{
if (!module) {
ERROR_LOG_REPORT(SCEMODULE, "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)
{
if (module->nm.status == MODULE_STATUS_STARTING)
module->nm.status = MODULE_STATUS_STARTED;
if (module->nm.status == MODULE_STATUS_STOPPING)
module->nm.status = MODULE_STATUS_STOPPED;
for (auto it = module->waitingThreads.begin(), end = module->waitingThreads.end(); it < end; ++it) {
// Still waiting?
if (HLEKernel::VerifyWait(it->threadID, WAITTYPE_MODULE, leftModuleID))
{
if (it->statusPtr != 0)
Memory::Write_U32(exitStatus, it->statusPtr);
__KernelResumeThreadFromWait(it->threadID, 0);
if (module->nm.status == MODULE_STATUS_UNLOADING) {
// TODO: Maybe should maintain the exitCode?
sceKernelDeleteThread(it->threadID);
} else {
if (it->statusPtr != 0)
Memory::Write_U32(exitStatus, it->statusPtr);
__KernelResumeThreadFromWait(it->threadID, 0);
}
}
}
module->waitingThreads.clear();
if (module->nm.status == MODULE_STATUS_UNLOADING) {
// TODO: Delete the waiting thread?
kernelObjects.Destroy<Module>(leftModuleID);
}
}
struct GetModuleIdByAddressArg