void HB_SetupBootloader(u32* cmdbuf) { if(!cmdbuf)return; if(cmdbuf[0] != 0x20042 || cmdbuf[2] != 0) { //send error cmdbuf[0]=0x00020040; cmdbuf[1]=0xFFFFFFFF; return; } const u32 memBlockAdr=cmdbuf[1]; const Handle processHandle=cmdbuf[3]; // map block to pre-0x00100000 address // TODO : make first half RX and second half RW // svc_controlProcessMemory(processHandle, 0x000F0000, memBlockAdr, 0x00008000, MEMOP_MAP, 0x7); svc_controlProcessMemory(processHandle, 0x00100000, 0x0, 0x00008000, MEMOP_PROTECT, 0x7); // extend .text/rodata/data/bss area... int i; for(i=0;i<CN_ADDPAGES;i++) { Result ret=svc_controlProcessMemory(processHandle, 0x00100000+(CN_TOTALPAGES+i)*0x1000, CN_ALLOCPAGES_ADR+0x1000*i, 0x1000, MEMOP_MAP, 0x7); if(ret)*(u32*)NULL=ret; } if(targetProcessHandle)svc_closeHandle(targetProcessHandle); targetProcessHandle=processHandle; //response cmdbuf[0]=0x00020040; cmdbuf[1]=0x00000000; }
Result PrepareDeallocateExtraHeap(u32* heapAddrOut, u32* heapPagesOut) { if(!hasExtraHeap) { *heapAddrOut = 0; *heapPagesOut = 0; return 0; } u32 endAddr = 0x00100000+CN_NEWTOTALPAGES*0x1000; // Unmap extended pages. u32 i; for(i=0; i<extraHeapPages; i++) { int ret = svc_controlProcessMemory(processHandle, endAddr+i*0x1000, extraHeapAddr+i*0x1000, 0x1000, MEMOP_UNMAP, 0x7); if(ret) return ret; } *heapAddrOut = extraHeapAddr; *heapPagesOut = extraHeapPages; //svc_closeHandle(processHandle); processHandle = 0; extraHeapAddr = 0; extraHeapPages = 0; hasExtraHeap = 0; return 0; }
void HB_ReprotectMemory(u32* cmdbuf) { if(!cmdbuf)return; const u32 reprotectAddr=cmdbuf[1]; const u32 reprotectPages=cmdbuf[2]; u32 mode=cmdbuf[3]&0x7; if(!mode)mode=0x7; if(reprotectAddr < 0x00108000 || reprotectAddr >= 0x10000000 || reprotectPages > 0x1000 || reprotectAddr+reprotectPages*0x1000 > 0x10000000) { //send error cmdbuf[0]=0x00090040; cmdbuf[1]=0xFFFFFFFF; return; } u32 ret=0; int i; for(i=0; i<reprotectPages && !ret; i++)ret=svc_controlProcessMemory(targetProcessHandle, reprotectAddr+i*0x1000, 0x0, 0x1000, MEMOP_PROTECT, mode); cmdbuf[0]=0x00090080; cmdbuf[1]=ret; //error code (if any) cmdbuf[2]=i; //number of pages successfully reprotected }
u32 protectRemoteMemory(Handle hProcess, void* addr, u32 size) { u32 outAddr = 0; return svc_controlProcessMemory(hProcess, addr, addr, size, 6, 7); }
int Load3DSX(Handle file, Handle process, void* baseAddr, u32 heapAddr) { // Extra heap must be deallocated before loading a new 3DSX. if(hasExtraHeap) return -5; u32 i, j, k, m; u32 endAddr = 0x00100000+CN_NEWTOTALPAGES*0x1000; SEC_ASSERT(baseAddr >= (void*)0x00100000); SEC_ASSERT((((u32) baseAddr) & 0xFFF) == 0); // page alignment _fseek(file, 0x0, SEEK_SET); _3DSX_Header hdr; if (_fread(&hdr, sizeof(hdr), file) != 0) return -1; if (hdr.magic != _3DSX_MAGIC) return -2; _3DSX_LoadInfo d; d.segSizes[0] = (hdr.codeSegSize+0xFFF) &~ 0xFFF; SEC_ASSERT(d.segSizes[0] >= hdr.codeSegSize); // int overflow d.segSizes[1] = (hdr.rodataSegSize+0xFFF) &~ 0xFFF; SEC_ASSERT(d.segSizes[1] >= hdr.rodataSegSize); // int overflow d.segSizes[2] = (hdr.dataSegSize+0xFFF) &~ 0xFFF; SEC_ASSERT(d.segSizes[2] >= hdr.dataSegSize); // int overflow // Map extra heap. u32 pagesRequired = d.segSizes[0]/0x1000 + d.segSizes[1]/0x1000 + d.segSizes[2]/0x1000; // XXX: int overflow u32 extendedPagesSize = 0; if(pagesRequired > CN_TOTAL3DSXPAGES) { if(svc_unmapProcessMemory(process, 0x00100000, 0x02000000))return -12; u32 extendedPages = pagesRequired - CN_TOTAL3DSXPAGES + 1; u32 i; for(i=0; i<extendedPages; i++) { if(svc_controlProcessMemory(process, endAddr+i*0x1000, heapAddr+i*0x1000, 0x1000, MEMOP_MAP, 0x7)) return -4; } if(svc_controlProcessMemory(process, heapAddr, 0, extendedPages*0x1000, MEMOP_PROTECT, 0x1)) return -5; processHandle = process; hasExtraHeap = 1; extraHeapAddr = heapAddr; extraHeapPages = extendedPages; extendedPagesSize = extraHeapPages*0x1000; endAddr += extendedPagesSize; if(svc_mapProcessMemory(process, 0x00100000, 0x02000000))return -13; } u32 offsets[2] = { d.segSizes[0], d.segSizes[0] + d.segSizes[1] }; d.segPtrs[0] = baseAddr; d.segPtrs[1] = (char*)d.segPtrs[0] + d.segSizes[0]; SEC_ASSERT((u32)d.segPtrs[1] >= d.segSizes[0]); // int overflow d.segPtrs[2] = (char*)d.segPtrs[1] + d.segSizes[1]; SEC_ASSERT((u32)d.segPtrs[2] >= d.segSizes[1]); // int overflow SEC_ASSERT((u32)d.segPtrs[2] < endAddr); // within user memory // Skip header for future compatibility. _fseek(file, hdr.headerSize, SEEK_SET); // Read the relocation headers SEC_ASSERT(hdr.dataSegSize >= hdr.bssSize); // int underflow u32* relocs = (u32*)((char*)d.segPtrs[2] + hdr.dataSegSize - hdr.bssSize); SEC_ASSERT((u32)relocs >= (u32)d.segPtrs[2]); // int overflow SEC_ASSERT((u32)relocs < endAddr); // within user memory u32 nRelocTables = hdr.relocHdrSize/4; u32 relocsEnd = (u32)(relocs + 3*nRelocTables); SEC_ASSERT((u32)relocsEnd >= (u32)relocs); // int overflow SEC_ASSERT((u32)relocsEnd < endAddr); // within user memory // XXX: Ensure enough RW pages exist at baseAddr to hold a memory block of length "totalSize". // This also checks whether the memory region overflows into IPC data or loader data. for (i = 0; i < 3; i ++) if (_fread(&relocs[i*nRelocTables], nRelocTables*4, file) != 0) return -3; // Read the segments if (_fread(d.segPtrs[0], hdr.codeSegSize, file) != 0) return -4; if (_fread(d.segPtrs[1], hdr.rodataSegSize, file) != 0) return -5; if (_fread(d.segPtrs[2], hdr.dataSegSize - hdr.bssSize, file) != 0) return -6; // Relocate the segments for (i = 0; i < 3; i ++) { for (j = 0; j < nRelocTables; j ++) { u32 nRelocs = relocs[i*nRelocTables+j]; if (j >= 2) { // We are not using this table - ignore it _fseek(file, nRelocs*sizeof(_3DSX_Reloc), SEEK_CUR); continue; } static _3DSX_Reloc relocTbl[RELOCBUFSIZE]; u32* pos = (u32*)d.segPtrs[i]; u32* endPos = pos + (d.segSizes[i]/4); SEC_ASSERT(((u32) endPos) < endAddr); // within user memory while (nRelocs) { u32 toDo = nRelocs > RELOCBUFSIZE ? RELOCBUFSIZE : nRelocs; nRelocs -= toDo; if (_fread(relocTbl, toDo*sizeof(_3DSX_Reloc), file) != 0) return -7; for (k = 0; k < toDo && pos < endPos; k ++) { pos += relocTbl[k].skip; u32 num_patches = relocTbl[k].patch; for (m = 0; m < num_patches && pos < endPos; m ++) { void* addr = TranslateAddr(*pos, &d, offsets); SEC_ASSERT(((u32) pos) < endAddr); // within user memory switch (j) { case 0: *pos = (u32)addr; break; case 1: *pos = (int)addr - (int)pos; break; } pos++; } } } } } // Detect and fill _prm structure u32* prmStruct = (u32*)baseAddr + 1; if(prmStruct[0]==0x6D72705F) { // Write service handle table pointer // the actual structure has to be filled out by cn_bootloader prmStruct[1] = (u32)__service_ptr; // XXX: other fields that need filling: // prmStruct[2] <-- __apt_appid (default: 0x300) // prmStruct[3] <-- __heap_size (default: 24*1024*1024) // prmStruct[4] <-- __gsp_heap_size (default: 32*1024*1024) // prmStruct[5] <-- __system_arglist (default: NULL) prmStruct[2] = 0x300; prmStruct[3] = 29*1024*1024 - extendedPagesSize; prmStruct[4] = 32*1024*1024; prmStruct[5] = CN_ARGCV_LOC; prmStruct[6] = RUNFLAG_APTWORKAROUND; //__system_runflags // XXX: Notes on __system_arglist: // Contains a pointer to a u32 specifying the number of arguments immediately followed // by the NULL-terminated argument strings themselves (no pointers). The first argument // should always be the path to the file we are booting. Example: // \x02\x00\x00\x00sd:/dir/file.3dsx\x00Argument1\x00 // Above corresponds to { "sd:/dir/file.3dsx", "Argument1" }. } // Protect memory at d.segPtrs[0] as CODE (r-x) -- npages = d.segSizes[0] / 0x1000 for(i=0;i<d.segSizes[0]>>12;i++)svc_controlProcessMemory(process, (u32)d.segPtrs[0]+i*0x1000, 0x0, 0x00001000, MEMOP_PROTECT, 0x5); // Protect memory at d.segPtrs[1] as RODATA (r--) -- npages = d.segSizes[1] / 0x1000 for(i=0;i<d.segSizes[1]>>12;i++)svc_controlProcessMemory(process, (u32)d.segPtrs[1]+i*0x1000, 0x0, 0x00001000, MEMOP_PROTECT, 0x1); // Protect memory at d.segPtrs[2] as DATA (rw-) -- npages = d.segSizes[2] / 0x1000 for(i=0;i<d.segSizes[2]>>12;i++)svc_controlProcessMemory(process, (u32)d.segPtrs[2]+i*0x1000, 0x0, 0x00001000, MEMOP_PROTECT, 0x3); //svc_closeHandle(process); TODO //svc_closeHandle(file); return 0; // Success. }