MochaPayload/source/ios_mcp/source/ipc.c

468 lines
18 KiB
C

/***************************************************************************
* 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 "../../common/kernel_commands.h"
#include "dk.h"
#include "fsa.h"
#include "imports.h"
#include "logger.h"
#include "svc.h"
#include "wupserver.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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_KILL_SERVER 0x03
#define IOCTL_MEMCPY 0x04
#define IOCTL_REPEATED_WRITE 0x05
#define IOCTL_KERN_READ32 0x06
#define IOCTL_KERN_WRITE32 0x07
#define IOCTL_READ_OTP 0x08
#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
#define IOCTLV_DK_PCHAR_ACTIVATE 0x30
static int ipcNodeKilled;
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_KILL_SERVER: {
ipcNodeKilled = 1;
wupserver_deinit();
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;
u32 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] = svcCustomKernelCommand(KERNEL_READ32, message->ioctl.buffer_in[0] + i * 4);
}
}
break;
}
case IOCTL_KERN_WRITE32: {
if ((message->ioctl.length_in < 8)) {
res = IOS_ERROR_INVALID_SIZE;
} else {
svcCustomKernelCommand(KERNEL_WRITE32, message->ioctl.buffer_in[0], message->ioctl.buffer_in[1]);
}
break;
}
case IOCTL_READ_OTP: {
if ((message->ioctl.length_io < 0x400)) {
res = IOS_ERROR_INVALID_SIZE;
} else {
svcCustomKernelCommand(KERNEL_READ_OTP, message->ioctl.buffer_io);
}
break;
}
//!--------------------------------------------------------------------------------------------------------------
//! FSA handles for better performance
//!--------------------------------------------------------------------------------------------------------------
//! TODO: add checks for i/o buffer length
case IOCTL_FSA_OPEN: {
message->ioctl.buffer_io[0] = svcOpen("/dev/fsa", 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_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;
}
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;
}
default:
res = IOS_ERROR_INVALID_ARG;
break;
}
return res;
}
static int ipc_ioctlv(ipcmessage *message) {
int res = 0;
switch (message->ioctlv.command) {
case IOCTLV_DK_PCHAR_ACTIVATE:
res = dkPCHARActivateSmd(message->ioctlv.vecs);
break;
default:
res = IOS_ERROR_INVALID_ARG;
break;
}
return res;
}
static int ipc_thread(void *) {
int res;
ipcmessage *message;
u32 messageQueue[0x10];
int queueId = svcCreateMessageQueue(messageQueue, sizeof(messageQueue) / 4);
if (svcRegisterResourceManager("/dev/iosuhax", queueId) == 0) {
while (!ipcNodeKilled) {
res = svcReceiveMessage(queueId, &message, 0);
if (res < 0) {
usleep(10000);
continue;
}
switch (message->command) {
case IOS_OPEN: {
res = 0;
break;
}
case IOS_CLOSE: {
res = 0;
break;
}
case IOS_IOCTL: {
res = ipc_ioctl(message);
break;
}
case IOS_IOCTLV: {
res = ipc_ioctlv(message);
break;
}
default: {
res = IOS_ERROR_UNKNOWN_VALUE;
break;
}
}
svcResourceReply(message, res);
}
}
svcDestroyMessageQueue(queueId);
return 0;
}
void ipc_init(void) {
ipcNodeKilled = 0;
int threadId = svcCreateThread(ipc_thread, 0, (u32 *) (threadStack + sizeof(threadStack)), sizeof(threadStack), 0x78, 1);
if (threadId >= 0)
svcStartThread(threadId);
}
void ipc_deinit(void) {
int fd = svcOpen("/dev/iosuhax", 0);
if (fd >= 0) {
int dummy = 0;
svcIoctl(fd, IOCTL_KILL_SERVER, &dummy, sizeof(dummy), &dummy, sizeof(dummy));
svcClose(fd);
}
}