#include "Iop_Sysmem.h" #include "Log.h" #include "Iop_SifMan.h" using namespace Iop; #define LOG_NAME ("iop_sysmem") #define FUNCTION_ALLOCATEMEMORY "AllocateMemory" #define FUNCTION_FREEMEMORY "FreeMemory" #define FUNCTION_PRINTF "printf" #define FUNCTION_QUERYMEMSIZE "QueryMemSize" #define FUNCTION_QUERYMAXFREEMEMSIZE "QueryMaxFreeMemSize" #define FUNCTION_QUERYTOTALFREEMEMSIZE "QueryTotalFreeMemSize" #define MIN_BLOCK_SIZE 0x100 CSysmem::CSysmem(uint8* ram, uint32 memoryBegin, uint32 memoryEnd, BlockListType& blocks, CStdio& stdio, CIoman& ioman, CSifMan& sifMan) : m_iopRam(ram) , m_stdio(stdio) , m_ioman(ioman) , m_blocks(blocks) , m_memoryBegin(memoryBegin) , m_memorySize(memoryEnd - memoryBegin) { //Initialize block map m_headBlockId = m_blocks.Allocate(); auto block = m_blocks[m_headBlockId]; block->address = m_memorySize; block->size = 0; block->nextBlockId = BlockListType::INVALID_ID; //Register sif module sifMan.RegisterModule(MODULE_ID, this); } std::string CSysmem::GetId() const { return "sysmem"; } std::string CSysmem::GetFunctionName(unsigned int functionId) const { switch(functionId) { case 4: return FUNCTION_ALLOCATEMEMORY; break; case 5: return FUNCTION_FREEMEMORY; break; case 6: return FUNCTION_QUERYMEMSIZE; break; case 7: return FUNCTION_QUERYMAXFREEMEMSIZE; break; case 8: return FUNCTION_QUERYTOTALFREEMEMSIZE; break; case 14: return FUNCTION_PRINTF; break; default: return "unknown"; break; } } void CSysmem::Invoke(CMIPS& context, unsigned int functionId) { switch(functionId) { case 4: context.m_State.nGPR[CMIPS::V0].nD0 = static_cast(AllocateMemory( context.m_State.nGPR[CMIPS::A1].nV[0], context.m_State.nGPR[CMIPS::A0].nV[0], context.m_State.nGPR[CMIPS::A2].nV[0])); break; case 5: context.m_State.nGPR[CMIPS::V0].nD0 = static_cast(FreeMemory( context.m_State.nGPR[CMIPS::A0].nV[0])); break; case 6: context.m_State.nGPR[CMIPS::V0].nD0 = m_memorySize; break; case 7: context.m_State.nGPR[CMIPS::V0].nD0 = QueryMaxFreeMemSize(); break; case 8: context.m_State.nGPR[CMIPS::V0].nD0 = QueryTotalFreeMemSize(); break; case 14: m_stdio.__printf(context); break; default: CLog::GetInstance().Warn(LOG_NAME, "(%08X): Unknown function (%d) called.\r\n", context.m_State.nPC, functionId); break; } } bool CSysmem::Invoke(uint32 method, uint32* args, uint32 argsSize, uint32* ret, uint32 retSize, uint8* ram) { //On a real console, this is implemented by the FileIO module //The functions that are available depend on the version of that module switch(method) { case 0x01: assert(retSize == 4); ret[0] = SifAllocate(args[0]); break; case 0x02: assert(argsSize == 4); ret[0] = SifFreeMemory(args[0]); break; case 0x03: assert(argsSize >= 4); assert(retSize == 4); ret[0] = SifLoadMemory(args[0], reinterpret_cast(args) + 4); break; case 0x04: assert(retSize == 4); ret[0] = SifAllocateSystemMemory(args[0], args[1], args[2]); break; case 0x05: ret[0] = m_memorySize; break; case 0x06: ret[0] = QueryMaxFreeMemSize(); break; case 0x07: ret[0] = QueryTotalFreeMemSize(); break; default: CLog::GetInstance().Warn(LOG_NAME, "Unknown method invoked (0x%08X).\r\n", method); break; } return true; } uint32 CSysmem::QueryTotalFreeMemSize() { uint32 totalSize = 0; uint32 begin = 0; auto nextBlockId = &m_headBlockId; auto nextBlock = m_blocks[*nextBlockId]; while(nextBlock != nullptr) { uint32 end = nextBlock->address; totalSize += (end - begin); begin = nextBlock->address + nextBlock->size; nextBlockId = &nextBlock->nextBlockId; nextBlock = m_blocks[*nextBlockId]; } return totalSize; } uint32 CSysmem::QueryMaxFreeMemSize() { uint32 maxSize = 0; uint32 begin = 0; auto nextBlockId = &m_headBlockId; auto nextBlock = m_blocks[*nextBlockId]; while(nextBlock != nullptr) { uint32 end = nextBlock->address; assert(end >= begin); if((end - begin) >= maxSize) { maxSize = end - begin; } begin = nextBlock->address + nextBlock->size; nextBlockId = &nextBlock->nextBlockId; nextBlock = m_blocks[*nextBlockId]; } assert(maxSize <= m_memorySize); return maxSize; } uint32 CSysmem::AllocateMemory(uint32 size, uint32 flags, uint32 wantedAddress) { CLog::GetInstance().Print(LOG_NAME, "AllocateMemory(size = 0x%08X, flags = 0x%08X, wantedAddress = 0x%08X);\r\n", size, flags, wantedAddress); const uint32 blockSize = MIN_BLOCK_SIZE; if(size > (0 - blockSize)) { //Will be aligned up to 0, return failure return 0; } size = ((size + (blockSize - 1)) / blockSize) * blockSize; if(flags == 0 || flags == 1) { uint32 begin = 0; uint32* nextBlockId = &m_headBlockId; auto nextBlock = m_blocks[*nextBlockId]; while(nextBlock != nullptr) { uint32 end = nextBlock->address; if((end - begin) >= size) { break; } begin = nextBlock->address + nextBlock->size; nextBlockId = &nextBlock->nextBlockId; nextBlock = m_blocks[*nextBlockId]; } if(nextBlock != nullptr) { uint32 newBlockId = m_blocks.Allocate(); assert(newBlockId != BlockListType::INVALID_ID); if(newBlockId == BlockListType::INVALID_ID) { return 0; } auto newBlock = m_blocks[newBlockId]; newBlock->address = begin; newBlock->size = size; newBlock->nextBlockId = *nextBlockId; *nextBlockId = newBlockId; return begin + m_memoryBegin; } } else if(flags == 2) { assert(wantedAddress != 0); wantedAddress -= m_memoryBegin; uint32 begin = 0; uint32* nextBlockId = &m_headBlockId; auto nextBlock = m_blocks[*nextBlockId]; while(nextBlock != nullptr) { uint32 end = nextBlock->address; if(begin > wantedAddress) { //Couldn't find suitable space nextBlock = nullptr; break; } if( (begin <= wantedAddress) && (wantedAddress < end) && ((end - begin) >= size)) { break; } begin = nextBlock->address + nextBlock->size; nextBlockId = &nextBlock->nextBlockId; nextBlock = m_blocks[*nextBlockId]; } if(nextBlock != nullptr) { uint32 newBlockId = m_blocks.Allocate(); assert(newBlockId != BlockListType::INVALID_ID); if(newBlockId == BlockListType::INVALID_ID) { return 0; } auto newBlock = m_blocks[newBlockId]; newBlock->address = wantedAddress; newBlock->size = size; newBlock->nextBlockId = *nextBlockId; *nextBlockId = newBlockId; return wantedAddress + m_memoryBegin; } } else { assert(0); } assert(0); return 0; } uint32 CSysmem::FreeMemory(uint32 address) { CLog::GetInstance().Print(LOG_NAME, "FreeMemory(address = 0x%08X);\r\n", address); address -= m_memoryBegin; //Search for block pointing at the address auto nextBlockId = &m_headBlockId; auto nextBlock = m_blocks[*nextBlockId]; while(nextBlock != nullptr) { if(nextBlock->address == address) { break; } nextBlockId = &nextBlock->nextBlockId; nextBlock = m_blocks[*nextBlockId]; } if(nextBlock != nullptr) { m_blocks.Free(*nextBlockId); *nextBlockId = nextBlock->nextBlockId; return 0; } else { CLog::GetInstance().Warn(LOG_NAME, "%s: Trying to unallocate an unexisting memory block (0x%08X).\r\n", __FUNCTION__, address); return -1; } } void CSysmem::DumpAllocList() { auto nextBlockId = &m_headBlockId; auto nextBlock = m_blocks[*nextBlockId]; CLog::GetInstance().Print(LOG_NAME, "Alloc List\r\n"); CLog::GetInstance().Print(LOG_NAME, "------------------------------\r\n"); while(nextBlock != nullptr) { CLog::GetInstance().Print(LOG_NAME, "addr: %08X, size: %08X\r\n", nextBlock->address, nextBlock->size); nextBlockId = &nextBlock->nextBlockId; nextBlock = m_blocks[*nextBlockId]; } CLog::GetInstance().Print(LOG_NAME, "------------------------------\r\n"); } uint32 CSysmem::SifAllocate(uint32 nSize) { uint32 result = AllocateMemory(nSize, 0, 0); CLog::GetInstance().Print(LOG_NAME, "result = 0x%08X = Allocate(size = 0x%08X);\r\n", result, nSize); return result; } uint32 CSysmem::SifAllocateSystemMemory(uint32 nSize, uint32 nFlags, uint32 nPtr) { uint32 result = AllocateMemory(nSize, nFlags, nPtr); CLog::GetInstance().Print(LOG_NAME, "result = 0x%08X = AllocateSystemMemory(flags = 0x%08X, size = 0x%08X, ptr = 0x%08X);\r\n", result, nFlags, nSize, nPtr); return result; } uint32 CSysmem::SifLoadMemory(uint32 address, const char* filePath) { CLog::GetInstance().Print(LOG_NAME, "LoadMemory(address = 0x%08X, filePath = '%s');\r\n", address, filePath); auto fd = m_ioman.Open(Ioman::CDevice::OPEN_FLAG_RDONLY, filePath); if(static_cast(fd) < 0) { return fd; } uint32 fileSize = m_ioman.Seek(fd, 0, CIoman::SEEK_DIR_END); m_ioman.Seek(fd, 0, CIoman::SEEK_DIR_SET); m_ioman.Read(fd, fileSize, m_iopRam + address); m_ioman.Close(fd); return 0; } uint32 CSysmem::SifFreeMemory(uint32 address) { CLog::GetInstance().Print(LOG_NAME, "FreeMemory(address = 0x%08X);\r\n", address); return FreeMemory(address); }