mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 02:29:51 +01:00
Modules: Match start/stop results, state checks and QueryModuleInfo checks
All from modules/startresult and modules/queryinfo: - A nonzero module_start result deletes the module. sceKernelStartModule returns 0 for 1 (no resident) and the value itself otherwise. - A nonzero module_stop result leaves the module started, and sceKernelStopModule returns CAN_NOT_STOP for 1 and the value otherwise. - Only a never-started module can be started (ERROR otherwise). Stopping one that was never started gives NOT_STARTED, and unloading a started module gives NOT_REMOVABLE. - sceKernelQueryModuleInfo checks the pointer before the id, and with an SDK version past 2.07 accepts only info sizes 0x40 and 0x60. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8c5e6cbd42
commit
d09368e1cf
2 files changed
+59
-18
No files matched your search
@@ -3138,6 +3138,11 @@ static const u32 SM_OPTION_ATTR_MASK = 0x00F06000;
|
||||
|
||||
// What sceKernelStartModule, sceKernelStopModule and the self-stop calls check in their option
|
||||
// before anything else.
|
||||
// Loaded modules have no status of their own here (it stays 0), so anything before STARTING is one.
|
||||
static bool IsModuleLoadedNotStarted(const PSPModule *module) {
|
||||
return module->nm.status < MODULE_STATUS_STARTING;
|
||||
}
|
||||
|
||||
static int ValidateSMOption(const SceKernelSMOption *option) {
|
||||
if ((u32)(sceKernelGetCompiledSdkVersion() & 0xFFFF0000) > 0x0207FFFF && option->size != sizeof(SceKernelSMOption)) {
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_SIZE;
|
||||
@@ -3340,10 +3345,9 @@ u32 sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValue
|
||||
if (returnValueAddr)
|
||||
Memory::WriteOrException_U32(0, returnValueAddr);
|
||||
return hleLogInfo(Log::sceModule, moduleId, "Faked module");
|
||||
} else if (module->nm.status == MODULE_STATUS_STARTED) {
|
||||
// TODO: Maybe should be SCE_KERNEL_ERROR_ALREADY_STARTED, but I get SCE_KERNEL_ERROR_ERROR.
|
||||
// But I also get crashes...
|
||||
return hleLogError(Log::sceModule, SCE_KERNEL_ERROR_ERROR);
|
||||
} else if (!IsModuleLoadedNotStarted(module)) {
|
||||
// Only a module that was never started can be, not a started or a stopped one (modules/startresult).
|
||||
return hleLogError(Log::sceModule, SCE_KERNEL_ERROR_ERROR, "module status %d", module->nm.status);
|
||||
} else {
|
||||
bool needsWait;
|
||||
int ret = __KernelStartModule(moduleId, argsize, argAddr, returnValueAddr, smoption.PtrOrNull(), &needsWait);
|
||||
@@ -3383,7 +3387,16 @@ static u32 sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 retur
|
||||
return hleLogInfo(Log::sceModule, 0, "faking");
|
||||
}
|
||||
if (module->nm.status != MODULE_STATUS_STARTED) {
|
||||
return hleLogError(Log::sceModule, SCE_KERNEL_ERROR_ALREADY_STOPPED, "already stopped");
|
||||
// modules/startresult.
|
||||
u32 stopError = SCE_KERNEL_ERROR_ERROR;
|
||||
if (IsModuleLoadedNotStarted(module)) {
|
||||
stopError = SCE_KERNEL_ERROR_NOT_STARTED;
|
||||
} else if (module->nm.status == MODULE_STATUS_STOPPED) {
|
||||
stopError = SCE_KERNEL_ERROR_ALREADY_STOPPED;
|
||||
} else if (module->nm.status == MODULE_STATUS_STOPPING) {
|
||||
stopError = SCE_KERNEL_ERROR_ALREADY_STOPPING;
|
||||
}
|
||||
return hleLogError(Log::sceModule, stopError, "module status %d", module->nm.status);
|
||||
}
|
||||
|
||||
u32 stopFunc = module->nm.module_stop_func;
|
||||
@@ -3419,6 +3432,10 @@ static u32 sceKernelUnloadModule(u32 moduleId) {
|
||||
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
|
||||
if (!module)
|
||||
return hleDelayResult(hleLogError(Log::sceModule, error), "module unloaded", 150);
|
||||
// A started module (or one starting or stopping) has to be stopped first (modules/startresult).
|
||||
if (!module->isFake && !IsModuleLoadedNotStarted(module) && module->nm.status != MODULE_STATUS_STOPPED) {
|
||||
return hleLogError(Log::sceModule, SCE_KERNEL_ERROR_NOT_REMOVABLE, "module status %d", module->nm.status);
|
||||
}
|
||||
|
||||
module->Cleanup();
|
||||
kernelObjects.Destroy<PSPModule>(moduleId);
|
||||
@@ -3536,11 +3553,28 @@ void __KernelReturnFromModuleFunc(MIPSState *mips) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't be starting and stopping at the same time, so no need to differentiate.
|
||||
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;
|
||||
// What the start or stop call returns (modules/startresult). A nonzero module_start result
|
||||
// gets rid of the module, and returns 0 for 1 (no resident) and the value itself otherwise.
|
||||
// A nonzero module_stop result leaves the module started; 1 means it refused to stop.
|
||||
u32 waitResult = 0;
|
||||
bool deleteModule = false;
|
||||
if (module->nm.status == MODULE_STATUS_STARTING) {
|
||||
if (exitStatus == 0) {
|
||||
module->nm.status = MODULE_STATUS_STARTED;
|
||||
waitResult = leftModuleID;
|
||||
} else {
|
||||
module->nm.status = MODULE_STATUS_STOPPED;
|
||||
waitResult = exitStatus == 1 ? 0 : exitStatus;
|
||||
deleteModule = true;
|
||||
}
|
||||
} else if (module->nm.status == MODULE_STATUS_STOPPING) {
|
||||
if (exitStatus == 0) {
|
||||
module->nm.status = MODULE_STATUS_STOPPED;
|
||||
} else {
|
||||
module->nm.status = MODULE_STATUS_STARTED;
|
||||
waitResult = exitStatus == 1 ? SCE_KERNEL_ERROR_CAN_NOT_STOP : exitStatus;
|
||||
}
|
||||
}
|
||||
for (auto it = module->waitingThreads.begin(), end = module->waitingThreads.end(); it < end; ++it) {
|
||||
// Still waiting?
|
||||
if (HLEKernel::VerifyWait(it->threadID, WAITTYPE_MODULE, leftModuleID))
|
||||
@@ -3551,7 +3585,7 @@ void __KernelReturnFromModuleFunc(MIPSState *mips) {
|
||||
} else {
|
||||
if (it->statusPtr != 0)
|
||||
Memory::WriteOrException_U32(exitStatus, it->statusPtr);
|
||||
__KernelResumeThreadFromWait(it->threadID, module->nm.status == MODULE_STATUS_STARTED ? leftModuleID : 0);
|
||||
__KernelResumeThreadFromWait(it->threadID, waitResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3583,7 +3617,7 @@ void __KernelReturnFromModuleFunc(MIPSState *mips) {
|
||||
}
|
||||
}
|
||||
|
||||
if (module->nm.status == MODULE_STATUS_UNLOADING) {
|
||||
if (module->nm.status == MODULE_STATUS_UNLOADING || deleteModule) {
|
||||
// TODO: Delete the waiting thread?
|
||||
module->Cleanup();
|
||||
kernelObjects.Destroy<PSPModule>(leftModuleID);
|
||||
@@ -3882,16 +3916,21 @@ SceUID sceKernelLoadModuleBufferUsbWlan(u32 size, u32 bufPtr, u32 flags, u32 lmo
|
||||
static u32 sceKernelQueryModuleInfo(u32 uid, u32 infoAddr)
|
||||
{
|
||||
DEBUG_LOG(Log::sceModule, "sceKernelQueryModuleInfo(%i, %08x)", uid, infoAddr);
|
||||
// The pointer is checked before the id, and once an SDK version past 2.07 is set, only the
|
||||
// two struct sizes are accepted (modules/queryinfo).
|
||||
if (!Memory::IsValidRange(infoAddr, 0x40)) {
|
||||
return hleLogError(Log::sceModule, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "bad infoAddr");
|
||||
}
|
||||
auto info = PSPPointer<ModuleInfo>::Create(infoAddr);
|
||||
if ((u32)(sceKernelGetCompiledSdkVersion() & 0xFFFF0000) > 0x0207FFFF && info->size != 0x40 && info->size != 0x60) {
|
||||
return hleLogError(Log::sceModule, SCE_KERNEL_ERROR_ILLEGAL_SIZE, "bad size %d", (int)info->size);
|
||||
}
|
||||
|
||||
u32 error;
|
||||
PSPModule *module = kernelObjects.Get<PSPModule>(uid, error);
|
||||
if (!module) {
|
||||
return error;
|
||||
return hleLogError(Log::sceModule, error);
|
||||
}
|
||||
if (!Memory::IsValidAddress(infoAddr)) {
|
||||
return hleLogError(Log::sceModule, -1, "bad infoAddr");
|
||||
}
|
||||
|
||||
auto info = PSPPointer<ModuleInfo>::Create(infoAddr);
|
||||
|
||||
memcpy(info->segmentaddr, module->nm.segmentaddr, sizeof(info->segmentaddr));
|
||||
memcpy(info->segmentsize, module->nm.segmentsize, sizeof(info->segmentsize));
|
||||
|
||||
@@ -361,7 +361,9 @@ tests_good = [
|
||||
"modules/idlist/idlist",
|
||||
"modules/loadrace/loadrace",
|
||||
"modules/nostart/nostart",
|
||||
"modules/queryinfo/queryinfo",
|
||||
"modules/startoptions/startoptions",
|
||||
"modules/startresult/startresult",
|
||||
"modules/unresolved/unresolved",
|
||||
"net/inet/blocking",
|
||||
"net/inet/addr",
|
||||
|
||||
Reference in new issue
Block a user