/* 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 jpcsp.HLE.CanBeNull; import jpcsp.HLE.HLEFunction; import jpcsp.HLE.HLEModule; import jpcsp.HLE.HLEUnimplemented; import jpcsp.HLE.PspString; import jpcsp.HLE.TPointer; import jpcsp.HLE.TPointer32; import java.util.HashMap; import static jpcsp.HLE.modules.SysMemUserForUser.PSP_SMEM_High; import static jpcsp.HLE.modules.SysMemUserForUser.PSP_SMEM_Low; import jpcsp.HLE.kernel.types.MemoryChunk; import jpcsp.HLE.kernel.types.MemoryChunkList; import jpcsp.HLE.kernel.types.SceKernelErrors; import jpcsp.HLE.modules.SysMemUserForUser.SysMemInfo; import jpcsp.HLE.Modules; import org.apache.log4j.Logger; public class sceHeap extends HLEModule { public static Logger log = Modules.getLogger("sceHeap"); protected final static int PSP_HEAP_ATTR_ADDR_HIGH = 0x4000; // Create the heap in high memory. protected final static int PSP_HEAP_ATTR_EXT = 0x8000; // Automatically extend the heap's memory. private HashMap<Integer, HeapInfo> heapMap; private final static int defaultAllocAlignment = 4; private static class HeapInfo { private SysMemInfo sysMemInfo; private MemoryChunkList freeMemoryChunks; private HashMap<Integer, MemoryChunk> allocatedMemoryChunks; private int allocType; public HeapInfo(SysMemInfo sysMemInfo) { this.sysMemInfo = sysMemInfo; MemoryChunk memoryChunk = new MemoryChunk(sysMemInfo.addr, sysMemInfo.size); freeMemoryChunks = new MemoryChunkList(memoryChunk); allocatedMemoryChunks = new HashMap<Integer, MemoryChunk>(); allocType = sysMemInfo.type; } public int alloc(int size, int alignment) { int allocatedAddr = 0; switch (allocType) { case PSP_SMEM_Low: allocatedAddr = freeMemoryChunks.allocLow(size, alignment - 1); break; case PSP_SMEM_High: allocatedAddr = freeMemoryChunks.allocHigh(size, alignment - 1); break; } if (allocatedAddr == 0) { return 0; } MemoryChunk memoryChunk = new MemoryChunk(allocatedAddr, size); allocatedMemoryChunks.put(allocatedAddr, memoryChunk); return allocatedAddr; } public boolean free(int addr) { MemoryChunk memoryChunk = allocatedMemoryChunks.remove(addr); if (memoryChunk == null) { return false; } freeMemoryChunks.add(memoryChunk); return true; } public void delete() { Modules.SysMemUserForUserModule.free(sysMemInfo); } @Override public String toString() { return String.format(String.format("HeapInfo 0x%08X, free=[%s]", sysMemInfo.addr, freeMemoryChunks)); } } @Override public void start() { heapMap = new HashMap<Integer, sceHeap.HeapInfo>(); super.start(); } @Override public void stop() { for (HeapInfo heapInfo : heapMap.values()) { heapInfo.delete(); } heapMap.clear(); super.stop(); } @HLEUnimplemented @HLEFunction(nid = 0x0E875980, version = 500, checkInsideInterrupt = true) public int sceHeapReallocHeapMemory(TPointer heapAddr, TPointer memAddr, int memSize) { return 0; } @HLEUnimplemented @HLEFunction(nid = 0x1C84B58D, version = 500, checkInsideInterrupt = true) public int sceHeapReallocHeapMemoryWithOption(TPointer heapAddr, TPointer memAddr, int memSize, TPointer paramAddr) { return 0; } @HLEFunction(nid = 0x2ABADC63, version = 500, checkInsideInterrupt = true) public int sceHeapFreeHeapMemory(TPointer heapAddr, TPointer memAddr) { // Try to free memory back to the heap. HeapInfo heapInfo = heapMap.get(heapAddr.getAddress()); if (heapInfo == null) { return SceKernelErrors.ERROR_INVALID_ID; } if (!heapInfo.free(memAddr.getAddress())) { return SceKernelErrors.ERROR_INVALID_POINTER; } if (log.isTraceEnabled()) { log.trace(String.format("sceHeapFreeHeapMemory after free: %s", heapInfo)); } return 0; } @HLEUnimplemented @HLEFunction(nid = 0x2A0C2009, version = 500, checkInsideInterrupt = true) public int sceHeapGetMallinfo(TPointer heapAddr, TPointer infoAddr) { return 0; } @HLEFunction(nid = 0x2B7299D8, version = 500, checkInsideInterrupt = true) public int sceHeapAllocHeapMemoryWithOption(TPointer heapAddr, int memSize, @CanBeNull TPointer32 paramAddr) { int alignment = defaultAllocAlignment; if (paramAddr.isNotNull()) { int paramSize = paramAddr.getValue(0); if (paramSize == 8) { alignment = paramAddr.getValue(4); if (log.isDebugEnabled()) { log.debug(String.format("sceHeapAllocHeapMemoryWithOption options: struct size=%d, alignment=0x%X", paramSize, alignment)); } } else { log.warn(String.format("sceHeapAllocHeapMemoryWithOption option at %s(size=%d)", paramAddr, paramSize)); } } // Try to allocate memory from the heap and return it's address. HeapInfo heapInfo = heapMap.get(heapAddr.getAddress()); if (heapInfo == null) { return 0; } int allocatedAddr = heapInfo.alloc(memSize, alignment); if (log.isTraceEnabled()) { log.trace(String.format("sceHeapAllocHeapMemoryWithOption returns 0x%08X, after allocation: %s", allocatedAddr, heapInfo)); } return allocatedAddr; } @HLEUnimplemented @HLEFunction(nid = 0x4929B40D, version = 500, checkInsideInterrupt = true) public int sceHeapGetTotalFreeSize(TPointer heapAddr) { return 0; } @HLEFunction(nid = 0x7012BBDD, version = 500, checkInsideInterrupt = true) public int sceHeapIsAllocatedHeapMemory(TPointer heapAddr, TPointer memAddr) { if (!heapMap.containsKey(heapAddr.getAddress())) { return SceKernelErrors.ERROR_INVALID_ID; } HeapInfo heapInfo = heapMap.get(heapAddr.getAddress()); if (heapInfo.allocatedMemoryChunks.containsKey(memAddr.getAddress())) { return 1; } return 0; } @HLEFunction(nid = 0x70210B73, version = 500, checkInsideInterrupt = true) public int sceHeapDeleteHeap(TPointer heapAddr) { HeapInfo heapInfo = heapMap.remove(heapAddr.getAddress()); if (heapInfo == null) { return SceKernelErrors.ERROR_INVALID_ID; } heapInfo.delete(); return 0; } @HLEFunction(nid = 0x7DE281C2, version = 500, checkInsideInterrupt = true) public int sceHeapCreateHeap(PspString name, int heapSize, int attr, @CanBeNull TPointer paramAddr) { if (paramAddr.isNotNull()) { log.warn(String.format("sceHeapCreateHeap unknown option at %s", paramAddr)); } int memType = PSP_SMEM_Low; if ((attr & PSP_HEAP_ATTR_ADDR_HIGH) == PSP_HEAP_ATTR_ADDR_HIGH) { memType = PSP_SMEM_High; } // Allocate a virtual heap memory space and return it's address. SysMemInfo info = null; int alignment = 4; int totalHeapSize = (heapSize + (alignment - 1)) & (~(alignment - 1)); int partitionId = SysMemUserForUser.USER_PARTITION_ID; int maxFreeSize = Modules.SysMemUserForUserModule.maxFreeMemSize(partitionId); if (totalHeapSize <= maxFreeSize) { info = Modules.SysMemUserForUserModule.malloc(partitionId, name.getString(), memType, totalHeapSize, 0); } else { log.warn(String.format("sceHeapCreateHeap not enough free mem (want=%d, free=%d, diff=%d)", totalHeapSize, maxFreeSize, totalHeapSize - maxFreeSize)); } if (info == null) { return 0; // Returns NULL on error. } HeapInfo heapInfo = new HeapInfo(info); heapMap.put(info.addr, heapInfo); return info.addr; } @HLEFunction(nid = 0xA8E102A0, version = 500, checkInsideInterrupt = true) public int sceHeapAllocHeapMemory(TPointer heapAddr, int memSize) { // Try to allocate memory from the heap and return it's address. HeapInfo heapInfo = heapMap.get(heapAddr.getAddress()); if (heapInfo == null) { return 0; } int allocatedAddr = heapInfo.alloc(memSize, defaultAllocAlignment); if (log.isTraceEnabled()) { log.trace(String.format("sceHeapAllocHeapMemory returns 0x%08X, after allocation: %s", allocatedAddr, heapInfo)); } return allocatedAddr; } }