/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.HLE.modules; import static jpcsp.Allegrex.Common._a0; import static jpcsp.Allegrex.Common._a1; import static jpcsp.Allegrex.Common._ra; import static jpcsp.Allegrex.Common._sp; import static jpcsp.HLE.kernel.types.SceKernelErrors.ERROR_ERRNO_FILE_NOT_FOUND; import static jpcsp.HLE.kernel.types.SceKernelErrors.ERROR_KERNEL_UNKNOWN_MODULE; import static jpcsp.HLE.modules.ThreadManForUser.ADDIU; import static jpcsp.HLE.modules.ThreadManForUser.J; import static jpcsp.HLE.modules.ThreadManForUser.JAL; import static jpcsp.HLE.modules.ThreadManForUser.LUI; import static jpcsp.HLE.modules.ThreadManForUser.LW; import static jpcsp.HLE.modules.ThreadManForUser.SW; import jpcsp.Allegrex.compiler.RuntimeContext; import jpcsp.HLE.CanBeNull; import jpcsp.HLE.HLEFunction; import jpcsp.HLE.HLELogging; import jpcsp.HLE.HLEModule; import jpcsp.HLE.HLEModuleManager; import jpcsp.HLE.HLEUnimplemented; import jpcsp.HLE.PspString; import jpcsp.HLE.TPointer; import jpcsp.HLE.TPointer32; import java.io.IOException; import java.nio.ByteBuffer; import jpcsp.Emulator; import jpcsp.Loader; import jpcsp.Memory; import jpcsp.MemoryMap; import jpcsp.HLE.Modules; import jpcsp.HLE.VFS.IVirtualFile; import jpcsp.HLE.VFS.IVirtualFileSystem; import jpcsp.HLE.kernel.Managers; import jpcsp.HLE.kernel.types.IAction; import jpcsp.HLE.kernel.types.SceIoStat; import jpcsp.HLE.kernel.types.SceKernelErrors; import jpcsp.HLE.kernel.types.SceKernelModuleInfo; import jpcsp.HLE.kernel.types.SceKernelLMOption; import jpcsp.HLE.kernel.types.SceKernelSMOption; import jpcsp.HLE.kernel.types.SceKernelThreadInfo; import jpcsp.HLE.kernel.types.SceModule; import jpcsp.HLE.modules.SysMemUserForUser.SysMemInfo; import jpcsp.filesystems.SeekableDataInput; import jpcsp.filesystems.umdiso.UmdIsoFile; import jpcsp.format.PSP; import jpcsp.memory.IMemoryReader; import jpcsp.memory.IMemoryWriter; import jpcsp.memory.MemoryReader; import jpcsp.memory.MemoryWriter; import jpcsp.util.Utilities; import org.apache.log4j.Logger; public class ModuleMgrForUser extends HLEModule { public static Logger log = Modules.getLogger("ModuleMgrForUser"); public static class LoadModuleContext { public String fileName; public String moduleName; public int flags; public int uid; public int buffer; public int bufferSize; public SceKernelLMOption lmOption; public boolean byUid; public boolean needModuleInfo; public boolean allocMem; public int baseAddr; public int basePartition; public SceKernelThreadInfo thread; public ByteBuffer moduleBuffer; public LoadModuleContext() { basePartition = SysMemUserForUser.USER_PARTITION_ID; } @Override public String toString() { return String.format("fileName='%s', moduleName='%s'", fileName, moduleName); } } public static final int loadHLEModuleDelay = 50000; // 50 ms delay protected int startModuleHandler; private SysMemInfo startOptionsMem; private TPointer startOptions; @Override public void start() { startModuleHandler = 0; startOptionsMem = null; startOptions = null; super.start(); } private class LoadModuleAction implements IAction { private LoadModuleContext loadModuleContext; public LoadModuleAction(LoadModuleContext loadModuleContext) { this.loadModuleContext = loadModuleContext; } @Override public void execute() { hleKernelLoadModuleNow(loadModuleContext); } } private int hleKernelLoadHLEModule(LoadModuleContext loadModuleContext) { String fileName = loadModuleContext.fileName; HLEModuleManager moduleManager = HLEModuleManager.getInstance(); // Extract the module name from the file name int startPrx = fileName.lastIndexOf("/"); int endPrx = fileName.toLowerCase().indexOf(".prx"); if (endPrx >= 0) { loadModuleContext.moduleName = fileName.substring(startPrx + 1, endPrx); } else { loadModuleContext.moduleName = fileName; } if (!moduleManager.hasFlash0Module(loadModuleContext.moduleName)) { // Retrieve the module name from the file content // if it could not be guessed from the file name. getModuleNameFromFileContent(loadModuleContext); } // Check if the module is not overwritten // by a file located under flash0 (decrypted from a real PSP). String modulePrxFileName = moduleManager.getModulePrxFileName(loadModuleContext.moduleName); if (modulePrxFileName != null) { StringBuilder localFileName = new StringBuilder(); IVirtualFileSystem vfs = Modules.IoFileMgrForUserModule.getVirtualFileSystem(modulePrxFileName, localFileName); if (vfs.ioGetstat(localFileName.toString(), new SceIoStat()) == 0) { // The flash0 file is available, load it loadModuleContext.fileName = modulePrxFileName; return -1; } } // Check if the PRX name matches an HLE module if (moduleManager.hasFlash0Module(loadModuleContext.moduleName)) { if (log.isInfoEnabled()) { log.info(String.format("hleKernelLoadModule(path='%s') HLE module %s loaded", loadModuleContext.fileName, loadModuleContext.moduleName)); } return moduleManager.LoadFlash0Module(loadModuleContext.moduleName); } return -1; } private void getModuleNameFromFileContent(LoadModuleContext loadModuleContext) { // Extract the library name from the file itself // for files in "~SCE"/"~PSP" format. SeekableDataInput file; if (loadModuleContext.byUid) { file = Modules.IoFileMgrForUserModule.getFile(loadModuleContext.uid); } else { file = Modules.IoFileMgrForUserModule.getFile(loadModuleContext.fileName, IoFileMgrForUser.PSP_O_RDONLY); } if (file == null) { return; } final int sceHeaderLength = 0x40; byte[] header = new byte[sceHeaderLength + PSP.PSP_HEADER_SIZE]; try { long position = file.getFilePointer(); file.readFully(header); file.seek(position); ByteBuffer f = ByteBuffer.wrap(header); // Skip an optional "~SCE" header int magic = Utilities.readWord(f); if (magic == Loader.SCE_MAGIC) { f.position(sceHeaderLength); } else { f.position(0); } // Retrieve the library name from the "~PSP" header PSP psp = new PSP(f); if (psp.isValid()) { String libName = psp.getModname(); if (libName != null && libName.length() > 0) { // We could extract the library name from the file loadModuleContext.moduleName = libName; if (log.isDebugEnabled()) { log.debug(String.format("getModuleNameFromFileContent %s", loadModuleContext)); } } } } catch (IOException e) { // Ignore exception } if (!loadModuleContext.byUid) { try { file.close(); } catch (IOException e) { } } } public int hleKernelLoadModule(LoadModuleContext loadModuleContext) { loadModuleContext.thread = Modules.ThreadManForUserModule.getCurrentThread(); IAction delayedLoadModule = new LoadModuleAction(loadModuleContext); Modules.ThreadManForUserModule.getCurrentThread().wait.Io_id = -1; Modules.ThreadManForUserModule.hleBlockCurrentThread(SceKernelThreadInfo.JPCSP_WAIT_IO); Emulator.getScheduler().addAction(Emulator.getClock().microTime() + 100000, delayedLoadModule); return 0; } public int hleKernelLoadAndStartModule(String name, int startPriority) { LoadModuleContext loadModuleContext = new LoadModuleContext(); loadModuleContext.fileName = name; loadModuleContext.allocMem = true; loadModuleContext.thread = Modules.ThreadManForUserModule.getCurrentThread(); int moduleUid = hleKernelLoadModuleNow(loadModuleContext); if (moduleUid >= 0) { if (startOptionsMem == null) { final int startOptionsSize = 20; startOptionsMem = Modules.SysMemUserForUserModule.malloc(SysMemUserForUser.KERNEL_PARTITION_ID, "ModuleStartOptions", SysMemUserForUser.PSP_SMEM_Low, startOptionsSize, 0); startOptions = new TPointer(Memory.getInstance(), startOptionsMem.addr); startOptions.setValue32(startOptionsSize); } SceKernelSMOption sceKernelSMOption = new SceKernelSMOption(); sceKernelSMOption.mpidStack = 0; sceKernelSMOption.stackSize = 0; sceKernelSMOption.attribute = 0; sceKernelSMOption.priority = startPriority; sceKernelSMOption.write(startOptions); hleKernelStartModule(moduleUid, 0, TPointer.NULL, TPointer32.NULL, startOptions, false); } return moduleUid; } private int hleKernelLoadModuleNow(LoadModuleContext loadModuleContext) { int result = delayedKernelLoadModule(loadModuleContext); if (loadModuleContext.thread != null) { loadModuleContext.thread.cpuContext._v0 = result; Modules.ThreadManForUserModule.hleUnblockThread(loadModuleContext.thread.uid); } return result; } public SceModule getModuleInfo(String name, ByteBuffer moduleBuffer, int mpidText, int mpidData) { SceModule module = null; try { module = Loader.getInstance().LoadModule(name, moduleBuffer, MemoryMap.START_USERSPACE, mpidText, mpidData, true, false, true); moduleBuffer.rewind(); } catch (IOException e) { log.error("getModuleRequiredMemorySize", e); } return module; } public int getModuleRequiredMemorySize(SceModule module) { if (module == null) { return 0; } return module.loadAddressHigh - module.loadAddressLow; } private int hleKernelLoadModuleFromModuleBuffer(LoadModuleContext loadModuleContext) throws IOException { int result; int moduleBase; int mpidText; int mpidData; SysMemInfo moduleInfo = null; if (loadModuleContext.allocMem) { mpidText = loadModuleContext.lmOption != null && loadModuleContext.lmOption.mpidText != 0 ? loadModuleContext.lmOption.mpidText : SysMemUserForUser.USER_PARTITION_ID; mpidData = loadModuleContext.lmOption != null && loadModuleContext.lmOption.mpidData != 0 ? loadModuleContext.lmOption.mpidData : SysMemUserForUser.USER_PARTITION_ID; final int allocType = loadModuleContext.lmOption != null ? loadModuleContext.lmOption.position : SysMemUserForUser.PSP_SMEM_Low; final int moduleHeaderSize = 256; // Load the module in analyze mode to find out its required memory size SceModule testModule = getModuleInfo(loadModuleContext.fileName, loadModuleContext.moduleBuffer, mpidText, mpidData); int totalAllocSize = moduleHeaderSize + getModuleRequiredMemorySize(testModule); if (log.isDebugEnabled()) { log.debug(String.format("Module '%s' requires %d bytes memory", loadModuleContext.fileName, totalAllocSize)); } // Take the partition IDs from the module information, if available if (loadModuleContext.lmOption == null || loadModuleContext.lmOption.mpidText == 0) { if (testModule.mpidtext != 0) { mpidText = testModule.mpidtext; } } if (loadModuleContext.lmOption == null || loadModuleContext.lmOption.mpidData == 0) { if (testModule.mpiddata != 0) { mpidData = testModule.mpiddata; } } SysMemInfo testInfo = Modules.SysMemUserForUserModule.malloc(mpidText, "ModuleMgr-TestInfo", allocType, totalAllocSize, 0); if (testInfo == null) { log.error(String.format("Failed module allocation of size 0x%08X for '%s' (maxFreeMemSize=0x%08X)", totalAllocSize, loadModuleContext.fileName, Modules.SysMemUserForUserModule.maxFreeMemSize(mpidText))); return -1; } int testBase = testInfo.addr; Modules.SysMemUserForUserModule.free(testInfo); // Allocate the memory for the memory header itself, // the space required by the module will be allocated by the Loader. if (loadModuleContext.needModuleInfo) { moduleInfo = Modules.SysMemUserForUserModule.malloc(mpidText, "ModuleMgr", SysMemUserForUser.PSP_SMEM_Addr, moduleHeaderSize, testBase); if (moduleInfo == null) { log.error(String.format("Failed module allocation 0x%08X != null for '%s'", testBase, loadModuleContext.fileName)); return -1; } if (moduleInfo.addr != testBase) { log.error(String.format("Failed module allocation 0x%08X != 0x%08X for '%s'", testBase, moduleInfo.addr, loadModuleContext.fileName)); return -1; } moduleBase = moduleInfo.addr + moduleHeaderSize; } else { moduleBase = testBase; } } else { moduleBase = loadModuleContext.baseAddr; mpidText = loadModuleContext.basePartition; mpidData = loadModuleContext.basePartition; } // Load the module SceModule module = Loader.getInstance().LoadModule(loadModuleContext.fileName, loadModuleContext.moduleBuffer, moduleBase, mpidText, mpidData, false, loadModuleContext.allocMem, true); module.load(); if ((module.fileFormat & Loader.FORMAT_SCE) == Loader.FORMAT_SCE || (module.fileFormat & Loader.FORMAT_PSP) == Loader.FORMAT_PSP) { // Simulate a successful loading log.info("hleKernelLoadModule(path='" + loadModuleContext.fileName + "') encrypted module not loaded"); SceModule fakeModule = new SceModule(true); fakeModule.modname = loadModuleContext.moduleName.toString(); fakeModule.addAllocatedMemory(moduleInfo); if (moduleInfo != null) { fakeModule.write(Memory.getInstance(), moduleInfo.addr); } Managers.modules.addModule(fakeModule); result = fakeModule.modid; } else if ((module.fileFormat & Loader.FORMAT_ELF) == Loader.FORMAT_ELF) { module.addAllocatedMemory(moduleInfo); result = module.modid; if (log.isDebugEnabled()) { log.debug(String.format("hleKernelLoadModule returning uid=0x%X", result)); } } else { // The Loader class now manages the module's memory footprint, it won't allocate if it failed to load result = -1; } return result; } private int delayedKernelLoadModule(LoadModuleContext loadModuleContext) { int result = hleKernelLoadHLEModule(loadModuleContext); if (result >= 0) { Modules.ThreadManForUserModule.hleKernelDelayThread(loadHLEModuleDelay, false); return result; } // Load module as ELF try { loadModuleContext.moduleBuffer = null; if (loadModuleContext.buffer != 0) { byte[] bytes = new byte[loadModuleContext.bufferSize]; IMemoryReader memoryReader = MemoryReader.getMemoryReader(loadModuleContext.buffer, loadModuleContext.bufferSize, 1); for (int i = 0; i < loadModuleContext.bufferSize; i++) { bytes[i] = (byte) memoryReader.readNext(); } loadModuleContext.moduleBuffer = ByteBuffer.wrap(bytes); } else { // TODO we need to properly handle the loading byUid (sceKernelLoadModuleByID) // where the module to be loaded is only a part of a big file. // We currently assume that the file contains only the module to be loaded. SeekableDataInput moduleInput = Modules.IoFileMgrForUserModule.getFile(loadModuleContext.fileName, loadModuleContext.flags); if (moduleInput != null) { if (moduleInput instanceof UmdIsoFile) { UmdIsoFile umdIsoFile = (UmdIsoFile) moduleInput; String realFileName = umdIsoFile.getName(); if (realFileName != null && !loadModuleContext.fileName.endsWith(realFileName)) { loadModuleContext.fileName = realFileName; result = hleKernelLoadHLEModule(loadModuleContext); if (result >= 0) { moduleInput.close(); return result; } } } byte[] moduleBytes = new byte[(int) moduleInput.length()]; moduleInput.readFully(moduleBytes); moduleInput.close(); loadModuleContext.moduleBuffer = ByteBuffer.wrap(moduleBytes); } } if (loadModuleContext.moduleBuffer != null) { result = hleKernelLoadModuleFromModuleBuffer(loadModuleContext); } else { log.warn(String.format("hleKernelLoadModule(path='%s') can't find file", loadModuleContext.fileName)); return ERROR_ERRNO_FILE_NOT_FOUND; } } catch (IOException e) { log.error(String.format("hleKernelLoadModule - Error while loading module %s", loadModuleContext.fileName), e); return -1; } return result; } public int hleKernelStartModule(int uid, int argSize, TPointer argp, TPointer32 statusAddr, TPointer optionAddr, boolean waitForThreadEnd) { SceModule sceModule = Managers.modules.getModuleByUID(uid); SceKernelSMOption smOption = null; if (optionAddr.isNotNull()) { smOption = new SceKernelSMOption(); smOption.read(optionAddr); } if (sceModule == null) { log.warn(String.format("sceKernelStartModule - unknown module UID 0x%X", uid)); return ERROR_KERNEL_UNKNOWN_MODULE; } if (log.isDebugEnabled()) { log.debug(String.format("sceKernelStartModule starting module '%s'", sceModule.modname)); } statusAddr.setValue(0); if (sceModule.isFlashModule) { // Trying to start a module loaded from flash0: // Do nothing... if (HLEModuleManager.getInstance().hasFlash0Module(sceModule.modname)) { log.info(String.format("IGNORING:sceKernelStartModule HLE module '%s'", sceModule.modname)); } else { log.warn(String.format("IGNORING:sceKernelStartModule flash module '%s'", sceModule.modname)); } sceModule.start(); return sceModule.modid; // return the module id } ThreadManForUser threadMan = Modules.ThreadManForUserModule; int attribute = sceModule.attribute; int entryAddr = sceModule.entry_addr; if (Memory.isAddressGood(sceModule.module_start_func)) { // Always take the module start function if one is defined. entryAddr = sceModule.module_start_func; if (sceModule.module_start_thread_attr != 0) { attribute = sceModule.module_start_thread_attr; } } if (Memory.isAddressGood(entryAddr)) { int priority = 0x20; if (smOption != null && smOption.priority > 0) { priority = smOption.priority; } else if (sceModule.module_start_thread_priority > 0) { priority = sceModule.module_start_thread_priority; } int stackSize = 0x40000; if (smOption != null && smOption.stackSize > 0) { stackSize = smOption.stackSize; } else if (sceModule.module_start_thread_stacksize > 0) { stackSize = sceModule.module_start_thread_stacksize; } int mpidStack = sceModule.mpiddata; if (smOption != null && smOption.mpidStack > 0) { mpidStack = smOption.mpidStack; } if (smOption != null && smOption.attribute != 0) { attribute = smOption.attribute; } // Remember the current thread as it can be changed by hleKernelStartThread. SceKernelThreadInfo currentThread = threadMan.getCurrentThread(); SceKernelThreadInfo thread = threadMan.hleKernelCreateThread("SceModmgrStart", entryAddr, priority, stackSize, attribute, 0, mpidStack); // override inherited module id with the new module we are starting thread.moduleid = sceModule.modid; // Store the thread exit status into statusAddr when the thread terminates thread.exitStatusAddr = statusAddr; sceModule.start(); if (startModuleHandler != 0) { // Install the start module handler so that it is called before the module entry point. final int numberInstructions = 12; final int newEntryAddr = thread.getStackAddr() + thread.stackSize - numberInstructions * 4; int moduleAddr1 = sceModule.address >>> 16; int moduleAddr2 = sceModule.address & 0xFFFF; if (moduleAddr2 >= 0x8000) { moduleAddr1 += 1; moduleAddr2 = (moduleAddr2 - 0x10000) & 0xFFFF; } IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(newEntryAddr, numberInstructions * 4, 4); memoryWriter.writeNext(ADDIU(_sp, _sp, -16)); memoryWriter.writeNext(SW (_a0, _sp, 0)); memoryWriter.writeNext(SW (_a1, _sp, 4)); memoryWriter.writeNext(SW (_ra, _sp, 8)); memoryWriter.writeNext(LUI (_a0, moduleAddr1)); memoryWriter.writeNext(JAL (startModuleHandler)); memoryWriter.writeNext(ADDIU(_a0, _a0, moduleAddr2)); memoryWriter.writeNext(LW (_a0, _sp, 0)); memoryWriter.writeNext(LW (_a1, _sp, 4)); memoryWriter.writeNext(LW (_ra, _sp, 8)); memoryWriter.writeNext(J (thread.entry_addr)); memoryWriter.writeNext(ADDIU(_sp, _sp, 16)); memoryWriter.flush(); thread.entry_addr = newEntryAddr; thread.preserveStack = true; // Do not overwrite above code RuntimeContext.invalidateRange(newEntryAddr, numberInstructions * 4); if (log.isDebugEnabled()) { log.debug(String.format("sceKernelStartModule installed hook to call startModuleHandler 0x%08X from 0x%08X for sceModule 0x%08X", startModuleHandler, newEntryAddr, sceModule.address)); } } // Start the module start thread threadMan.hleKernelStartThread(thread, argSize, argp.getAddress(), sceModule.gp_value); if (waitForThreadEnd) { // Wait for the end of the module start thread. // Do no return the thread exit status as the result of this call, // return the module ID. threadMan.hleKernelWaitThreadEnd(currentThread, thread.uid, TPointer32.NULL, false, false); } } else if (entryAddr == 0 || entryAddr == -1) { Modules.log.info("sceKernelStartModule - no entry address"); sceModule.start(); } else { log.warn(String.format("sceKernelStartModule - invalid entry address 0x%08X", entryAddr)); return -1; } return sceModule.modid; } protected int getSelfModuleId() { return Modules.ThreadManForUserModule.getCurrentThread().moduleid; } public int hleKernelUnloadModule(int uid) { SceModule sceModule = Managers.modules.getModuleByUID(uid); if (sceModule == null) { log.warn(String.format("hleKernelUnloadModule unknown module UID 0x%X", uid)); return -1; } if (sceModule.isModuleStarted() && !sceModule.isModuleStopped()) { log.warn(String.format("hleKernelUnloadModule module 0x%X is still running!", uid)); return SceKernelErrors.ERROR_KERNEL_MODULE_CANNOT_REMOVE; } if (log.isDebugEnabled()) { log.debug(String.format("hleKernelUnloadModule '%s'", sceModule.modname)); } sceModule.unload(); HLEModuleManager.getInstance().UnloadFlash0Module(sceModule); return sceModule.modid; } @HLEFunction(nid = 0xB7F46618, version = 150, checkInsideInterrupt = true) public int sceKernelLoadModuleByID(int uid, @CanBeNull TPointer optionAddr) { String name = Modules.IoFileMgrForUserModule.getFileFilename(uid); if (log.isDebugEnabled()) { log.debug(String.format("sceKernelLoadModuleByID name='%s'", name)); } SceKernelLMOption lmOption = null; if (optionAddr.isNotNull()) { lmOption = new SceKernelLMOption(); lmOption.read(optionAddr); if (log.isInfoEnabled()) { log.info(String.format("sceKernelLoadModuleByID options: %s", lmOption)); } } LoadModuleContext loadModuleContext = new LoadModuleContext(); loadModuleContext.fileName = name; loadModuleContext.uid = uid; loadModuleContext.lmOption = lmOption; loadModuleContext.byUid = true; loadModuleContext.needModuleInfo = true; loadModuleContext.allocMem = true; return hleKernelLoadModule(loadModuleContext); } @HLEFunction(nid = 0x977DE386, version = 150, checkInsideInterrupt = true) public int sceKernelLoadModule(PspString path, int flags, @CanBeNull TPointer optionAddr) { SceKernelLMOption lmOption = null; if (optionAddr.isNotNull()) { lmOption = new SceKernelLMOption(); lmOption.read(optionAddr); if (log.isInfoEnabled()) { log.info(String.format("sceKernelLoadModule options: %s", lmOption)); } } LoadModuleContext loadModuleContext = new LoadModuleContext(); loadModuleContext.fileName = path.getString(); loadModuleContext.flags = flags; loadModuleContext.lmOption = lmOption; loadModuleContext.needModuleInfo = true; loadModuleContext.allocMem = true; return hleKernelLoadModule(loadModuleContext); } @HLEUnimplemented @HLEFunction(nid = 0x710F61B5, version = 150, checkInsideInterrupt = true) public int sceKernelLoadModuleMs() { return 0; } @HLEUnimplemented @HLEFunction(nid = 0xF9275D98, version = 150, checkInsideInterrupt = true) public int sceKernelLoadModuleBufferUsbWlan() { return 0; } @HLEFunction(nid = 0x50F0C1EC, version = 150, checkInsideInterrupt = true) public int sceKernelStartModule(int uid, int argSize, @CanBeNull TPointer argp, @CanBeNull TPointer32 statusAddr, @CanBeNull TPointer optionAddr) { return hleKernelStartModule(uid, argSize, argp, statusAddr, optionAddr, true); } @HLELogging(level="info") @HLEFunction(nid = 0xD1FF982A, version = 150, checkInsideInterrupt = true) public int sceKernelStopModule(int uid, int argSize, @CanBeNull TPointer argp, @CanBeNull TPointer32 statusAddr, @CanBeNull TPointer optionAddr) { SceModule sceModule = Managers.modules.getModuleByUID(uid); SceKernelSMOption smOption = null; if (optionAddr.isNotNull()) { smOption = new SceKernelSMOption(); smOption.read(optionAddr); } if (sceModule == null) { log.warn("sceKernelStopModule - unknown module UID 0x" + Integer.toHexString(uid)); return ERROR_KERNEL_UNKNOWN_MODULE; } statusAddr.setValue(0); if (log.isDebugEnabled()) { log.debug(String.format("sceKernelStopModule '%s'", sceModule.modname)); } if (sceModule.isFlashModule) { // Trying to stop a module loaded from flash0: // Shouldn't get here... if (HLEModuleManager.getInstance().hasFlash0Module(sceModule.modname)) { log.info("IGNORING:sceKernelStopModule HLE module '" + sceModule.modname + "'"); } else { log.warn("IGNORING:sceKernelStopModule flash module '" + sceModule.modname + "'"); } sceModule.stop(); return 0; // Fake success. } if (Memory.isAddressGood(sceModule.module_stop_func)) { int priority = 0x20; if (smOption != null && smOption.priority > 0) { priority = smOption.priority; } else if (sceModule.module_stop_thread_priority > 0) { priority = sceModule.module_stop_thread_priority; } int stackSize = 0x40000; if (smOption != null && smOption.stackSize > 0) { stackSize = smOption.stackSize; } else if (sceModule.module_stop_thread_stacksize > 0) { stackSize = sceModule.module_stop_thread_stacksize; } int mpidStack = sceModule.mpiddata; if (smOption != null && smOption.mpidStack > 0) { mpidStack = smOption.mpidStack; } int attribute = sceModule.module_stop_thread_attr; if (smOption != null) { attribute = smOption.attribute; } ThreadManForUser threadMan = Modules.ThreadManForUserModule; SceKernelThreadInfo currentThread = threadMan.getCurrentThread(); SceKernelThreadInfo thread = threadMan.hleKernelCreateThread("SceModmgrStop", sceModule.module_stop_func, priority, stackSize, attribute, 0, mpidStack); thread.moduleid = sceModule.modid; // Store the thread exit status into statusAddr when the thread terminates thread.exitStatusAddr = statusAddr; sceModule.stop(); // Start the "stop" thread... threadMan.hleKernelStartThread(thread, argSize, argp.getAddress(), sceModule.gp_value); // ...and wait for its end. threadMan.hleKernelWaitThreadEnd(currentThread, thread.uid, TPointer32.NULL, false, false); } else if (sceModule.module_stop_func == 0) { log.info("sceKernelStopModule - module has no stop function"); sceModule.stop(); } else if (sceModule.isModuleStopped()) { log.warn("sceKernelStopModule - module already stopped"); return SceKernelErrors.ERROR_KERNEL_MODULE_ALREADY_STOPPED; } else { log.warn(String.format("sceKernelStopModule - invalid stop function 0x%08X", sceModule.module_stop_func)); return -1; } return 0; } @HLELogging(level="info") @HLEFunction(nid = 0x2E0911AA, version = 150, checkInsideInterrupt = true) public int sceKernelUnloadModule(int uid) { return hleKernelUnloadModule(uid); } @HLEFunction(nid = 0xD675EBB8, version = 150, checkInsideInterrupt = true) public int sceKernelSelfStopUnloadModule(int exitCode, int argSize, @CanBeNull TPointer argp) { SceModule sceModule = Managers.modules.getModuleByUID(getSelfModuleId()); ThreadManForUser threadMan = Modules.ThreadManForUserModule; SceKernelThreadInfo thread = null; if (Memory.isAddressGood(sceModule.module_stop_func)) { // Start the module stop thread function. thread = threadMan.hleKernelCreateThread("SceModmgrStop", sceModule.module_stop_func, sceModule.module_stop_thread_priority, sceModule.module_stop_thread_stacksize, sceModule.module_stop_thread_attr, 0, sceModule.mpiddata); thread.moduleid = sceModule.modid; // Unload the module when the stop thread will be deleted thread.unloadModuleAtDeletion = true; } else { // Stop and unload the module immediately sceModule.stop(); sceModule.unload(); } threadMan.hleKernelExitDeleteThread(); // Delete the current thread. if (thread != null) { threadMan.hleKernelStartThread(thread, argSize, argp.getAddress(), sceModule.gp_value); } return 0; } @HLEFunction(nid = 0x8F2DF740, version = 150, checkInsideInterrupt = true) public int sceKernelStopUnloadSelfModuleWithStatus(int exitCode, int argSize, @CanBeNull TPointer argp, @CanBeNull TPointer32 statusAddr, @CanBeNull TPointer optionAddr) { SceModule sceModule = Managers.modules.getModuleByUID(getSelfModuleId()); if (log.isInfoEnabled()) { log.info(String.format("sceKernelStopUnloadSelfModuleWithStatus %s, exitCode=0x%X", sceModule, exitCode)); } ThreadManForUser threadMan = Modules.ThreadManForUserModule; SceKernelThreadInfo thread = null; statusAddr.setValue(0); if (Memory.isAddressGood(sceModule.module_stop_func)) { // Start the module stop thread function. statusAddr.setValue(0); // TODO set to return value of the thread (when it exits, of course) thread = threadMan.hleKernelCreateThread("SceModmgrStop", sceModule.module_stop_func, sceModule.module_stop_thread_priority, sceModule.module_stop_thread_stacksize, sceModule.module_stop_thread_attr, optionAddr.getAddress(), sceModule.mpiddata); thread.moduleid = sceModule.modid; // Store the thread exit status into statusAddr when the thread terminates thread.exitStatusAddr = statusAddr; threadMan.getCurrentThread().exitStatus = exitCode; // Set the current thread's exit status. // Unload the module when the stop thread will be deleted thread.unloadModuleAtDeletion = true; } else { // Stop and unload the module immediately sceModule.stop(); sceModule.unload(); } threadMan.hleKernelExitDeleteThread(); // Delete the current thread. if (thread != null) { threadMan.hleKernelStartThread(thread, argSize, argp.getAddress(), sceModule.gp_value); } return 0; } @HLEFunction(nid = 0xCC1D3699, version = 150, checkInsideInterrupt = true) public int sceKernelStopUnloadSelfModule(int argSize, @CanBeNull TPointer argp, @CanBeNull TPointer32 statusAddr, @CanBeNull TPointer optionAddr) { SceModule sceModule = Managers.modules.getModuleByUID(getSelfModuleId()); if (log.isInfoEnabled()) { log.info(String.format("sceKernelStopUnloadSelfModule %s", sceModule)); } ThreadManForUser threadMan = Modules.ThreadManForUserModule; SceKernelThreadInfo thread = null; statusAddr.setValue(0); if (Memory.isAddressGood(sceModule.module_stop_func)) { // Start the module stop thread function. thread = threadMan.hleKernelCreateThread("SceModmgrStop", sceModule.module_stop_func, sceModule.module_stop_thread_priority, sceModule.module_stop_thread_stacksize, sceModule.module_stop_thread_attr, optionAddr.getAddress(), sceModule.mpiddata); thread.moduleid = sceModule.modid; // Store the thread exit status into statusAddr when the thread terminates thread.exitStatusAddr = statusAddr; // Unload the module when the stop thread will be deleted thread.unloadModuleAtDeletion = true; } else { // Stop and unload the module immediately sceModule.stop(); sceModule.unload(); } threadMan.hleKernelExitDeleteThread(); // Delete the current thread. if (thread != null) { threadMan.hleKernelStartThread(thread, argSize, argp.getAddress(), sceModule.gp_value); } return 0; } /** * Get a list of module IDs. * @param resultBuffer Buffer to store the module list * @param resultBufferSize Number of bytes in the resultBuffer * @param idCountAddr Returns the number of module ids * @return >= 0 on success */ @HLEFunction(nid = 0x644395E2, version = 150, checkInsideInterrupt = true) public int sceKernelGetModuleIdList(TPointer32 resultBuffer, int resultBufferSize, TPointer32 idCountAddr) { int idCount = 0; int resultBufferOffset = 0; for (SceModule module : Managers.modules.values()) { if (!module.isFlashModule && module.isLoaded) { if (resultBufferOffset < resultBufferSize) { resultBuffer.setValue(resultBufferOffset, module.modid); resultBufferOffset += 4; } idCount++; } } idCountAddr.setValue(idCount); return 0; } @HLEFunction(nid = 0x748CBED9, version = 150, checkInsideInterrupt = true) public int sceKernelQueryModuleInfo(int uid, TPointer infoAddr) { SceModule sceModule = Managers.modules.getModuleByUID(uid); if (sceModule == null) { log.warn("sceKernelQueryModuleInfo unknown module UID 0x" + Integer.toHexString(uid)); return -1; } SceKernelModuleInfo moduleInfo = new SceKernelModuleInfo(); moduleInfo.copy(sceModule); moduleInfo.write(infoAddr); if (log.isDebugEnabled()) { log.debug(String.format("sceKernelQueryModuleInfo returning %s", Utilities.getMemoryDump(infoAddr.getAddress(), infoAddr.getValue32()))); } return 0; } @HLEFunction(nid = 0xF0A26395, version = 150, checkInsideInterrupt = true) public int sceKernelGetModuleId() { int moduleId = getSelfModuleId(); if (log.isDebugEnabled()) { log.debug(String.format("sceKernelGetModuleId returning 0x%X", moduleId)); } return moduleId; } @HLEFunction(nid = 0xD8B73127, version = 150, checkInsideInterrupt = true) public int sceKernelGetModuleIdByAddress(TPointer addr) { SceModule module = Managers.modules.getModuleByAddress(addr.getAddress()); if (module == null) { log.warn(String.format("sceKernelGetModuleIdByAddress addr=%s module not found", addr)); return -1; } return module.modid; } @HLEFunction(nid = 0xA1A78C58, version = 150) public int sceKernelLoadModuleDisc(PspString path, int flags, @CanBeNull TPointer optionAddr) { SceKernelLMOption lmOption = null; if (optionAddr.isNotNull()) { lmOption = new SceKernelLMOption(); lmOption.read(optionAddr); if (log.isInfoEnabled()) { log.info(String.format("sceKernelLoadModuleDisc options: %s", lmOption)); } } LoadModuleContext loadModuleContext = new LoadModuleContext(); loadModuleContext.fileName = path.getString(); loadModuleContext.flags = flags; loadModuleContext.lmOption = lmOption; loadModuleContext.allocMem = true; return hleKernelLoadModule(loadModuleContext); } /** * Sets a function to be called just before module_start of a module is gonna be called (useful for patching purposes) * * @param startModuleHandler - The function, that will receive the module structure before the module is started. * * @returns - The previous set function (NULL if none); * @Note: because only one handler function is handled by HEN, you should * call the previous function in your code. * * @Example: * * STMOD_HANDLER previous = NULL; * * int OnModuleStart(SceModule2 *mod); * * void somepointofmycode() * { * previous = sctrlHENSetStartModuleHandler(OnModuleStart); * } * * int OnModuleStart(SceModule2 *mod) * { * if (strcmp(mod->modname, "vsh_module") == 0) * { * // Do something with vsh module here * } * * if (!previous) * return 0; * * // Call previous handler * * return previous(mod); * } * * @Note2: The above example should be compiled with the flag -fno-pic * in order to avoid problems with gp register that may lead to a crash. * */ @HLEFunction(nid = 0x1C90BECB, version = 150) public int sctrlHENSetStartModuleHandler(TPointer startModuleHandler) { int previousStartModuleHandler = this.startModuleHandler; this.startModuleHandler = startModuleHandler.getAddress(); return previousStartModuleHandler; } /** * Finds a driver * * @param drvname - The name of the driver (without ":" or numbers) * * @returns the driver if found, NULL otherwise * */ @HLEUnimplemented @HLEFunction(nid = 0x78E46415, version = 150) public int sctrlHENFindDriver(String drvname) { return 0; } @HLEUnimplemented @HLEFunction(nid = 0xFEF27DC1, version = 271, checkInsideInterrupt = true) public int sceKernelLoadModuleDNAS(PspString path, TPointer key, int unknown, @CanBeNull TPointer32 optionAddr) { SceKernelLMOption lmOption = null; if (optionAddr.isNotNull()) { lmOption = new SceKernelLMOption(); lmOption.read(optionAddr); if (log.isInfoEnabled()) { log.info(String.format("sceKernelLoadModuleDNAS options: %s", lmOption)); } } StringBuilder localFileName = new StringBuilder(); IVirtualFileSystem vfs = Modules.IoFileMgrForUserModule.getVirtualFileSystem(path.getString(), localFileName); if (vfs != null) { IVirtualFile vFile = vfs.ioOpen(localFileName.toString(), IoFileMgrForUser.PSP_O_RDONLY, 0); if (vFile == null) { return ERROR_ERRNO_FILE_NOT_FOUND; } } else { return SceKernelErrors.ERROR_ERRNO_DEVICE_NOT_FOUND; } return 0; } @HLEFunction(nid = 0xF2D8D1B4, version = 271) public int sceKernelLoadModuleNpDrm(PspString path, int flags, @CanBeNull TPointer optionAddr) { SceKernelLMOption lmOption = null; if (optionAddr.isNotNull()) { lmOption = new SceKernelLMOption(); lmOption.read(optionAddr); if (log.isInfoEnabled()) { log.info(String.format("sceKernelLoadModuleNpDrm options: %s", lmOption)); } } // SPRX modules can't be decrypted yet. if (!Modules.scePspNpDrm_userModule.getDisableDLCStatus()) { log.warn(String.format("sceKernelLoadModuleNpDrm detected encrypted DLC module: %s", path.getString())); return SceKernelErrors.ERROR_NPDRM_INVALID_PERM; } LoadModuleContext loadModuleContext = new LoadModuleContext(); loadModuleContext.fileName = path.getString(); loadModuleContext.flags = flags; loadModuleContext.lmOption = lmOption; loadModuleContext.needModuleInfo = true; loadModuleContext.allocMem = true; return hleKernelLoadModule(loadModuleContext); } @HLEUnimplemented @HLEFunction(nid = 0xE4C4211C, version = 150, checkInsideInterrupt = true) public int sceKernelLoadModuleWithBlockOffset(PspString path, int memoryBlockId, int memoryBlockOffset, int flags) { return 0; } @HLEFunction(nid = 0xD2FBC957, version = 150) public int sceKernelGetModuleGPByAddress(int address, TPointer32 gpAddr) { SceModule module = Managers.modules.getModuleByAddress(address); if (module == null) { log.warn(String.format("sceKernelGetModuleGPByAddress not found module address=0x%08X", address)); return -1; } gpAddr.setValue(module.gp_value); return 0; } @HLEFunction(nid = 0x22BDBEFF, version = 150, checkInsideInterrupt = true) public int sceKernelQueryModuleInfo_660(int uid, TPointer infoAddr) { return sceKernelQueryModuleInfo(uid, infoAddr); } }