diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index ad92eed4dd..a4028a092a 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -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(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(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(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::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(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::Create(infoAddr); memcpy(info->segmentaddr, module->nm.segmentaddr, sizeof(info->segmentaddr)); memcpy(info->segmentsize, module->nm.segmentsize, sizeof(info->segmentsize)); diff --git a/test.py b/test.py index d949916a6a..69f7f7cde9 100755 --- a/test.py +++ b/test.py @@ -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",