/*************************************************************************** * Copyright (C) 2016 * by Dimok * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any * damages arising from the use of this software. * * Permission is granted to anyone to use this software for any * purpose, including commercial applications, and to alter it and * redistribute it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you * must not claim that you wrote the original software. If you use * this software in a product, an acknowledgment in the product * documentation would be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and * must not be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. ***************************************************************************/ #include #include #include #include "imports.h" #include "fsa.h" #include "svc.h" //#include "text.h" //#include "logger.h" #include "fsa.h" #define IOS_ERROR_UNKNOWN_VALUE 0xFFFFFFD6 #define IOS_ERROR_INVALID_ARG 0xFFFFFFE3 #define IOS_ERROR_INVALID_SIZE 0xFFFFFFE9 #define IOS_ERROR_UNKNOWN 0xFFFFFFF7 #define IOS_ERROR_NOEXISTS 0xFFFFFFFA #define IOCTL_MEM_WRITE 0x00 #define IOCTL_MEM_READ 0x01 #define IOCTL_SVC 0x02 #define IOCTL_MEMCPY 0x04 #define IOCTL_REPEATED_WRITE 0x05 #define IOCTL_KERN_READ32 0x06 #define IOCTL_KERN_WRITE32 0x07 #define IOCTL_FSA_OPEN 0x40 #define IOCTL_FSA_CLOSE 0x41 #define IOCTL_FSA_MOUNT 0x42 #define IOCTL_FSA_UNMOUNT 0x43 #define IOCTL_FSA_GETDEVICEINFO 0x44 #define IOCTL_FSA_OPENDIR 0x45 #define IOCTL_FSA_READDIR 0x46 #define IOCTL_FSA_CLOSEDIR 0x47 #define IOCTL_FSA_MAKEDIR 0x48 #define IOCTL_FSA_OPENFILE 0x49 #define IOCTL_FSA_READFILE 0x4A #define IOCTL_FSA_WRITEFILE 0x4B #define IOCTL_FSA_STATFILE 0x4C #define IOCTL_FSA_CLOSEFILE 0x4D #define IOCTL_FSA_SETFILEPOS 0x4E #define IOCTL_FSA_GETSTAT 0x4F #define IOCTL_FSA_REMOVE 0x50 #define IOCTL_FSA_REWINDDIR 0x51 #define IOCTL_FSA_CHDIR 0x52 #define IOCTL_FSA_RENAME 0x53 #define IOCTL_FSA_RAW_OPEN 0x54 #define IOCTL_FSA_RAW_READ 0x55 #define IOCTL_FSA_RAW_WRITE 0x56 #define IOCTL_FSA_RAW_CLOSE 0x57 #define IOCTL_FSA_CHANGEMODE 0x58 #define IOCTL_FSA_FLUSHVOLUME 0x59 //static u8 threadStack[0x1000] __attribute__((aligned(0x20))); static int ipc_ioctl(ipcmessage *message) { int res = 0; switch(message->ioctl.command) { case IOCTL_MEM_WRITE: { if(message->ioctl.length_in < 4) { res = IOS_ERROR_INVALID_SIZE; } else { memcpy((void*)message->ioctl.buffer_in[0], message->ioctl.buffer_in + 1, message->ioctl.length_in - 4); } break; } case IOCTL_MEM_READ: { if(message->ioctl.length_in < 4) { res = IOS_ERROR_INVALID_SIZE; } else { memcpy(message->ioctl.buffer_io, (void*)message->ioctl.buffer_in[0], message->ioctl.length_io); } break; } case IOCTL_SVC: { if((message->ioctl.length_in < 4) || (message->ioctl.length_io < 4)) { res = IOS_ERROR_INVALID_SIZE; } else { int svc_id = message->ioctl.buffer_in[0]; int size_arguments = message->ioctl.length_in - 4; u32 arguments[8]; memset(arguments, 0x00, sizeof(arguments)); memcpy(arguments, message->ioctl.buffer_in + 1, (size_arguments < 8 * 4) ? size_arguments : (8 * 4)); // return error code as data message->ioctl.buffer_io[0] = ((int (*const)(u32, u32, u32, u32, u32, u32, u32, u32))(MCP_SVC_BASE + svc_id * 8))(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]); } break; } case IOCTL_MEMCPY: { if(message->ioctl.length_in < 12) { res = IOS_ERROR_INVALID_SIZE; } else { memcpy((void*)message->ioctl.buffer_in[0], (void*)message->ioctl.buffer_in[1], message->ioctl.buffer_in[2]); } break; } case IOCTL_REPEATED_WRITE: { if(message->ioctl.length_in < 12) { res = IOS_ERROR_INVALID_SIZE; } else { u32* dst = (u32*)message->ioctl.buffer_in[0]; u32* cache_range = (u32*)(message->ioctl.buffer_in[0] & ~0xFF); u32 value = message->ioctl.buffer_in[1]; u32 n = message->ioctl.buffer_in[2]; u32 old = *dst; int i; for(i = 0; i < n; i++) { if(*dst != old) { if(*dst == 0x0) old = *dst; else { *dst = value; svcFlushDCache(cache_range, 0x100); break; } }else { svcInvalidateDCache(cache_range, 0x100); usleep(50); } } } break; } case IOCTL_KERN_READ32: { if((message->ioctl.length_in < 4) || (message->ioctl.length_io < 4)) { res = IOS_ERROR_INVALID_SIZE; } else { for(u32 i = 0; i < (message->ioctl.length_io/4); i++) { message->ioctl.buffer_io[i] = svcRead32(message->ioctl.buffer_in[0] + i * 4); } } break; } case IOCTL_KERN_WRITE32: { //! TODO: add syscall as on kern_read32 res = IOS_ERROR_NOEXISTS; break; } //!-------------------------------------------------------------------------------------------------------------- //! FSA handles for better performance //!-------------------------------------------------------------------------------------------------------------- //! TODO: add checks for i/o buffer length case IOCTL_FSA_OPEN: { // points to "/dev/fsa" string in mcp data section message->ioctl.buffer_io[0] = svcOpen((char*)0x0506963C, 0); break; } case IOCTL_FSA_CLOSE: { int fd = message->ioctl.buffer_in[0]; message->ioctl.buffer_io[0] = svcClose(fd); break; } case IOCTL_FSA_MOUNT: { int fd = message->ioctl.buffer_in[0]; char *device_path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; char *volume_path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[2]; u32 flags = message->ioctl.buffer_in[3]; char *arg_string = (message->ioctl.buffer_in[4] > 0) ? (((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[4]) : 0; int arg_string_len = message->ioctl.buffer_in[5]; message->ioctl.buffer_io[0] = FSA_Mount(fd, device_path, volume_path, flags, arg_string, arg_string_len); break; } case IOCTL_FSA_UNMOUNT: { int fd = message->ioctl.buffer_in[0]; char *device_path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; u32 flags = message->ioctl.buffer_in[2]; message->ioctl.buffer_io[0] = FSA_Unmount(fd, device_path, flags); break; } case IOCTL_FSA_FLUSHVOLUME: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_FlushVolume(fd, path); break; } case IOCTL_FSA_GETDEVICEINFO: { int fd = message->ioctl.buffer_in[0]; char *device_path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; int type = message->ioctl.buffer_in[2]; message->ioctl.buffer_io[0] = FSA_GetDeviceInfo(fd, device_path, type, message->ioctl.buffer_io + 1); break; } case IOCTL_FSA_OPENDIR: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_OpenDir(fd, path, (int*)message->ioctl.buffer_io + 1); break; } case IOCTL_FSA_READDIR: { int fd = message->ioctl.buffer_in[0]; int handle = message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_ReadDir(fd, handle, (directoryEntry_s*)(message->ioctl.buffer_io + 1)); break; } case IOCTL_FSA_CLOSEDIR: { int fd = message->ioctl.buffer_in[0]; int handle = message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_CloseDir(fd, handle); break; } case IOCTL_FSA_MAKEDIR: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; u32 flags = message->ioctl.buffer_in[2]; message->ioctl.buffer_io[0] = FSA_MakeDir(fd, path, flags); break; } case IOCTL_FSA_OPENFILE: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; char *mode = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[2]; message->ioctl.buffer_io[0] = FSA_OpenFile(fd, path, mode, (int*)message->ioctl.buffer_io + 1); break; } case IOCTL_FSA_READFILE: { int fd = message->ioctl.buffer_in[0]; u32 size = message->ioctl.buffer_in[1]; u32 cnt = message->ioctl.buffer_in[2]; int fileHandle = message->ioctl.buffer_in[3]; u32 flags = message->ioctl.buffer_in[4]; message->ioctl.buffer_io[0] = FSA_ReadFile(fd, ((u8*)message->ioctl.buffer_io) + 0x40, size, cnt, fileHandle, flags); break; } case IOCTL_FSA_WRITEFILE: { int fd = message->ioctl.buffer_in[0]; u32 size = message->ioctl.buffer_in[1]; u32 cnt = message->ioctl.buffer_in[2]; int fileHandle = message->ioctl.buffer_in[3]; u32 flags = message->ioctl.buffer_in[4]; message->ioctl.buffer_io[0] = FSA_WriteFile(fd, ((u8*)message->ioctl.buffer_in) + 0x40, size, cnt, fileHandle, flags); break; } case IOCTL_FSA_STATFILE: { int fd = message->ioctl.buffer_in[0]; int fileHandle = message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_StatFile(fd, fileHandle, (fileStat_s*)(message->ioctl.buffer_io + 1)); break; } case IOCTL_FSA_CLOSEFILE: { int fd = message->ioctl.buffer_in[0]; int fileHandle = message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_CloseFile(fd, fileHandle); break; } case IOCTL_FSA_SETFILEPOS: { int fd = message->ioctl.buffer_in[0]; int fileHandle = message->ioctl.buffer_in[1]; u32 position = message->ioctl.buffer_in[2]; message->ioctl.buffer_io[0] = FSA_SetPosFile(fd, fileHandle, position); break; } case IOCTL_FSA_GETSTAT: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_GetStat(fd, path, (fileStat_s*)(message->ioctl.buffer_io + 1)); break; } case IOCTL_FSA_REMOVE: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_Remove(fd, path); break; } case IOCTL_FSA_REWINDDIR: { int fd = message->ioctl.buffer_in[0]; int dirFd = message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_RewindDir(fd, dirFd); break; } case IOCTL_FSA_CHDIR: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_ChangeDir(fd, path); break; } case IOCTL_FSA_RAW_OPEN: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_RawOpen(fd, path, (int*)(message->ioctl.buffer_io + 1)); break; } case IOCTL_FSA_RAW_READ: { int fd = message->ioctl.buffer_in[0]; u32 block_size = message->ioctl.buffer_in[1]; u32 cnt = message->ioctl.buffer_in[2]; u64 sector_offset = ((u64)message->ioctl.buffer_in[3] << 32ULL) | message->ioctl.buffer_in[4]; int deviceHandle = message->ioctl.buffer_in[5]; message->ioctl.buffer_io[0] = FSA_RawRead(fd, ((u8*)message->ioctl.buffer_io) + 0x40, block_size, cnt, sector_offset, deviceHandle); break; } case IOCTL_FSA_RAW_WRITE: { int fd = message->ioctl.buffer_in[0]; u32 block_size = message->ioctl.buffer_in[1]; u32 cnt = message->ioctl.buffer_in[2]; u64 sector_offset = ((u64)message->ioctl.buffer_in[3] << 32ULL) | message->ioctl.buffer_in[4]; int deviceHandle = message->ioctl.buffer_in[5]; message->ioctl.buffer_io[0] = FSA_RawWrite(fd, ((u8*)message->ioctl.buffer_in) + 0x40, block_size, cnt, sector_offset, deviceHandle); break; } case IOCTL_FSA_RAW_CLOSE: { int fd = message->ioctl.buffer_in[0]; int deviceHandle = message->ioctl.buffer_in[1]; message->ioctl.buffer_io[0] = FSA_RawClose(fd, deviceHandle); break; } case IOCTL_FSA_CHANGEMODE: { int fd = message->ioctl.buffer_in[0]; char *path = ((char *)message->ioctl.buffer_in) + message->ioctl.buffer_in[1]; int mode = message->ioctl.buffer_in[2]; message->ioctl.buffer_io[0] = FSA_ChangeMode(fd, path, mode); break; } default: res = IOS_ERROR_INVALID_ARG; break; } return res; } int ipc_thread(void *arg) { int res; ipcmessage *message; /*u32* messageQueue = svcAllocAlign(0xCAFF, 0x20, 0x20); int queueId = svcCreateMessageQueue(messageQueue, 0x10);*/ // mcp main thread message queue listening on "/dev/mcp" int queueId = *(int*)0x5070AEC; int exit = 0; while(!exit) { res = svcReceiveMessage(queueId, &message, 0); if(res < 0) { usleep(10000); continue; } switch(message->command) { case IOS_OPEN: { //log_printf("IOS_OPEN\n"); res = 0; break; } case IOS_CLOSE: { //log_printf("IOS_CLOSE\n"); exit = 1; res = 0; break; } case IOS_IOCTL: { //log_printf("IOS_IOCTL\n"); res = ipc_ioctl(message); break; } case IOS_IOCTLV: { //log_printf("IOS_IOCTLV\n"); res = 0; break; } default: { //log_printf("unexpected command 0x%X\n", message->command); res = IOS_ERROR_UNKNOWN_VALUE; break; } } svcResourceReply(message, res); } return res; } /*void ipc_init(void) { int threadId = svcCreateThread(ipc_thread, 0, (u32*)(threadStack + sizeof(threadStack)), sizeof(threadStack), 0x78, 1); if(threadId >= 0) svcStartThread(threadId); }*/