Add DK_PCHAR support to /dev/iosuhax

This commit is contained in:
GaryOderNichts 2023-02-11 02:22:14 +01:00 committed by Maschell
parent 939b1837e4
commit 68c60ac2d7
16 changed files with 1082 additions and 121 deletions

View File

@ -108,7 +108,7 @@ void instant_patches_setup(void) {
// Place the environment path at the end of our .text section.
for (int i = 0; i < 0x100; i += 4) {
*(volatile u32 *) (0x05119F00 - 0x05100000 + 0x13D80000 + i) = *(volatile u32 *) (0x0017FEF0 + i);
*(volatile u32 *) (0x0511FF00 - 0x05100000 + 0x13D80000 + i) = *(volatile u32 *) (0x0017FEF0 + i);
}
// force check USB storage on load
@ -134,7 +134,7 @@ void instant_patches_setup(void) {
map_info.paddr = 0x05116000 - 0x05100000 + 0x13D80000;
map_info.vaddr = 0x05116000;
map_info.size = 0x4000;
map_info.size = 0xA000;
map_info.domain = 1; // MCP
map_info.type = 3; // 0 = undefined, 1 = kernel only, 2 = read only, 3 = read write
map_info.cached = 0xFFFFFFFF;

View File

@ -22,7 +22,6 @@
* distribution.
***************************************************************************/
#include "ios_mcp_patches.h"
#include "../../ios_mcp/ios_mcp.bin.h"
#include "../../ios_mcp/ios_mcp_syms.h"
#include "elf_patcher.h"
#include "types.h"
@ -41,7 +40,7 @@ void mcp_run_patches(u32 ios_elf_start) {
section_write_bss(ios_elf_start, _bss_start, _bss_end - _bss_start);
// We can't use "_text_end" here because we need to copy the full 0x4000 to preserve the envrionmen path which
// is at the end of the .text section.
section_write(ios_elf_start, _text_start, (void *) mcp_get_phys_code_base(), 0x4000);
section_write(ios_elf_start, _text_start, (void *) mcp_get_phys_code_base(), 0xA000);
u32 patch_count = (u32) (((u8 *) mcp_patches_table_end) - ((u8 *) mcp_patches_table)) / sizeof(patch_table_t);
patch_table_entries(ios_elf_start, mcp_patches_table, patch_count);

66
source/ios_mcp/imports.ld Normal file
View File

@ -0,0 +1,66 @@
PROVIDE(printf = 0x0503dcc0);
/* PROVIDE(printf = 0x05055438); */
PROVIDE(memcmp = 0x05054d6c);
PROVIDE(memcpy = 0x05054e54);
PROVIDE(memset = 0x05054ef0);
PROVIDE(vsnprintf = 0x05055c40);
/*PROVIDE(snprintf = 0x05055c8c);*/
PROVIDE(strncpy = 0x05055db4);
PROVIDE(strnlen = 0x05055e94);
PROVIDE(strncmp = 0x05055e10);
PROVIDE(usleep = 0x050564e4);
PROVIDE(bspWrite = 0x0503d460);
PROVIDE(bspRead = 0x0503d550);
PROVIDE(UCWriteSysConfig = 0x05044a8c);
PROVIDE(UCReadSysConfig = 0x05044d5c);
PROVIDE(UCClose = 0x05045024);
PROVIDE(UCOpen = 0x05045028);
PROVIDE(IOS_CreateThread = 0x050567ec);
PROVIDE(IOS_JoinThread = 0x050567f4);
PROVIDE(IOS_CancelThread = 0x050567fc);
PROVIDE(IOS_StartThread = 0x05056824);
PROVIDE(IOS_GetThreadPriority = 0x0505683c);
PROVIDE(IOS_CreateMessageQueue = 0x0505684c);
PROVIDE(IOS_DestroyMessageQueue = 0x05056854);
PROVIDE(IOS_ReceiveMessage = 0x0505686c);
PROVIDE(IOS_CreateTimer = 0x05056884);
PROVIDE(IOS_RestartTimer = 0x0505688c);
PROVIDE(IOS_StopTimer = 0x05056894);
PROVIDE(IOS_DestroyTimer = 0x0505689c);
PROVIDE(IOS_CheckDebugMode = 0x050568ec);
PROVIDE(IOS_ReadOTP = 0x050568fc);
PROVIDE(IOS_HeapAlloc = 0x05056924);
PROVIDE(IOS_HeapAllocAligned = 0x0505692c);
PROVIDE(IOS_HeapFree = 0x05056934);
PROVIDE(IOS_Open = 0x05056984);
PROVIDE(IOS_Close = 0x0505698c);
PROVIDE(IOS_Ioctl = 0x050569ac);
PROVIDE(IOS_Ioctlv = 0x050569b4);
PROVIDE(IOS_IoctlvAsync = 0x050569ec);
PROVIDE(IOS_InvalidateDCache = 0x05056a74);
PROVIDE(IOS_FlushDCache = 0x05056a7c);
PROVIDE(IOS_CreateSemaphore = 0x05056aa4);
PROVIDE(IOS_WaitSemaphore = 0x05056aac);
PROVIDE(IOS_SignalSemaphore = 0x05056ab4);
PROVIDE(IOS_DestroySempahore = 0x05056abc);
PROVIDE(IOS_VirtToPhys = 0x05056a9c);
PROVIDE(IOS_CheckIOPAddressRange = 0x05056ad4);
PROVIDE(IOS_Shutdown = 0x05056b7c);
PROVIDE(IOS_Syscall0x81 = 0x05056bf4);
PROVIDE(ppcHeartBeatThreadId = 0x05070458);
PROVIDE(currentColdbootOS = 0x050b8174);
PROVIDE(currentColdbootTitle = 0x050b817c);

View File

@ -1,5 +1,7 @@
OUTPUT_ARCH(arm)
INCLUDE "imports.ld"
PROVIDE(snprintf = 0x05059010);
SECTIONS
@ -22,6 +24,6 @@ SECTIONS
}
}
ASSERT((SIZEOF(.text)) < 0x3F00, "text section is too big");
ASSERT((SIZEOF(.text)) < 0x9F00, "text section is too big");
ASSERT((SIZEOF(.bss)) < 0x3000, "bss section is too big");

370
source/ios_mcp/source/dk.c Normal file
View File

@ -0,0 +1,370 @@
#include "dk.h"
#include "imports.h"
#include "smd.h"
#include "socket.h"
#include "svc.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
//#define DEBUG_LOGGING
#define DK_PORT 3000
#define DK_THREAD_INTERVAL 600
#define DK_SMD_IDX_READ 0
#define DK_SMD_IDX_WRITE 1
static u8 threadStack[0x500] __attribute__((aligned(0x20)));
static int threadRunning = 0;
static int threadId = 0;
static int servfd = 0;
static int setupServer(int port) {
if (socketInit() < 0) {
printf("DK: failed to init socketlib\n");
smdIopClose(DK_SMD_IDX_READ);
smdIopClose(DK_SMD_IDX_WRITE);
return -1;
}
printf("DK: socket init\n");
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
printf("DK: failed to create socket\n");
smdIopClose(DK_SMD_IDX_READ);
smdIopClose(DK_SMD_IDX_WRITE);
return -1;
}
printf("DK: socket created\n");
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
printf("DK warning: failed to set SO_REUSEADDR\n");
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = port;
if (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) != 0) {
printf("DK: failed to bind socket\n");
goto quit;
}
printf("DK: socket bound\n");
return sockfd;
quit:;
closesocket(sockfd);
return -1;
}
static int handleServer(int connfd) {
int nonblock = 1;
if (setsockopt(connfd, SOL_SOCKET, SO_NONBLOCK, &nonblock, sizeof(nonblock)) < 0) {
printf("DK: failed to make connection non-blocking\n");
return 0;
}
int sack = 1;
if (setsockopt(connfd, SOL_SOCKET, SO_TCPSACK, &sack, sizeof(sack)) < 0) {
printf("DK: failed to set SO_TCPSACK\n");
return 0;
}
int nodelay = 1;
if (setsockopt(connfd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) < 0) {
printf("DK: failed to set TCP_NODELAY\n");
return 0;
}
// create a message queue and timer
uint32_t messageBuf;
int queueId = IOS_CreateMessageQueue(&messageBuf, 1);
if (queueId < 0) {
printf("DK: failed to create message queue\n");
return -1;
}
int timerId = IOS_CreateTimer(0, 0, queueId, 0xdeafcafe);
if (timerId < 0) {
printf("DK: failed to create timer\n");
IOS_DestroyMessageQueue(queueId);
return -1;
}
while (threadRunning) {
// restart timer
if (IOS_RestartTimer(timerId, DK_THREAD_INTERVAL, 0)) {
printf("DK: failed to restart timer\n");
break;
}
// wait until the timer sends a message
uint32_t message;
if (IOS_ReceiveMessage(queueId, &message, 0) < 0) {
printf("DK: failed to receive message\n");
break;
}
SmdInterfaceState readState;
if (smdIopGetInterfaceState(DK_SMD_IDX_READ, NULL, &readState) != 0) {
printf("DK: Failed to get interface state, closing...\n");
break;
}
SmdInterfaceState writeState;
if (smdIopGetInterfaceState(DK_SMD_IDX_WRITE, NULL, &writeState) != 0) {
printf("DK: Failed to get interface state, closing...\n");
break;
}
if (readState != SMD_INTERFACE_STATE_OPENED || writeState != SMD_INTERFACE_STATE_OPENED) {
printf("DK: Closing interfaces\n");
break;
}
// process up to 100 writes before waiting
for (int i = 0; i < 100; ++i) {
SmdReceiveData receiveData;
if (smdIopReceive(DK_SMD_IDX_WRITE, &receiveData) != 0) {
break;
}
#ifdef DEBUG_LOGGING
printf("DK: received write vector\n");
#endif
if (receiveData.type != SMD_ELEMENT_TYPE_VECTOR) {
printf("DK Warn: Received smd type %ld on write queue\n", receiveData.type);
continue;
}
if (receiveData.vector->id != DK_PCHAR_COMMAND_WRITE) {
printf("DK Warn: Received command %lx on write queue\n", receiveData.vector->id);
smdIopSendVector(DK_SMD_IDX_WRITE, receiveData.vector);
continue;
}
DKPCHARResult *result = receiveData.vector->vecs[0].ptr;
DKPCHARResponse *response = receiveData.vector->vecs[2].ptr;
response->buf = receiveData.vector->vecs[1].ptr;
// send data
int32_t error = 0;
uint32_t to_send = receiveData.vector->vecs[1].size;
#ifdef DEBUG_LOGGING
printf("DK: sending %lu bytes\n", to_send);
#endif
uint32_t offset = 0;
while (to_send > 0) {
int res = send(connfd, receiveData.vector->vecs[1].ptr + offset, to_send, 0);
if (res < 0) {
error = res;
break;
}
to_send -= res;
offset += res;
}
result->error = error;
smdIopSendVector(DK_SMD_IDX_WRITE, receiveData.vector);
}
// read pending data, if we have any
for (int i = 0; i < 100; ++i) {
char buf[500];
int32_t res = recv(connfd, buf, sizeof(buf), 0);
if (res == -0xafffa /*EWOULDBLOCK*/) {
// no more data to receive
break;
}
if (res < 0) {
printf("DK: recv returned %lx\n", res);
break;
}
if (res == 0) {
printf("DK: client disconnected\n");
IOS_DestroyTimer(timerId);
IOS_DestroyMessageQueue(queueId);
return 0;
}
#ifdef DEBUG_LOGGING
printf("DK: recv: %ld\n", res);
#endif
SmdReceiveData receiveData;
if (smdIopReceive(DK_SMD_IDX_READ, &receiveData) != 0) {
printf("DK Warn: no vector in read queue, dropping packet\n");
break;
}
#ifdef DEBUG_LOGGING
printf("DK: received read vector\n");
#endif
if (receiveData.type != SMD_ELEMENT_TYPE_VECTOR) {
printf("DK Warn: Received smd type %ld on read queue\n", receiveData.type);
continue;
}
if (receiveData.vector->id != DK_PCHAR_COMMAND_READ) {
printf("DK Warn: Received command %lx on read queue\n", receiveData.vector->id);
smdIopSendVector(DK_SMD_IDX_READ, receiveData.vector);
continue;
}
DKPCHARResult *result = receiveData.vector->vecs[0].ptr;
DKPCHARResponse *response = receiveData.vector->vecs[2].ptr;
response->buf = receiveData.vector->vecs[1].ptr;
if (receiveData.vector->vecs[1].size < (uint32_t) res) {
printf("DK: Warning truncating received data\n");
res = receiveData.vector->vecs[1].size;
}
if (res > 0) {
memcpy(receiveData.vector->vecs[1].ptr, buf, res);
result->error = 0;
result->param0 = res;
} else {
result->error = res;
}
smdIopSendVector(DK_SMD_IDX_READ, receiveData.vector);
}
}
IOS_DestroyTimer(timerId);
IOS_DestroyMessageQueue(queueId);
return -1;
}
static int dkThread(void *arg) {
printf("DK: thread started\n");
threadRunning = 1;
servfd = setupServer(DK_PORT);
if (servfd < 0) {
goto exit;
}
while (threadRunning) {
if (listen(servfd, 1) != 0) {
printf("DK: failed listening for connections\n");
goto exit;
}
printf("DK: listening for connections on port %d\n", DK_PORT);
struct sockaddr_in clientaddr;
socklen_t len = sizeof(clientaddr);
int connfd = accept(servfd, (struct sockaddr *) &clientaddr, &len);
if (connfd < 0) {
printf("DK: failed to accept connection\n");
continue;
}
uint8_t *addr = (uint8_t *) &clientaddr.sin_addr.s_addr;
printf("DK: Connected to %d.%d.%d.%d\n", addr[0], addr[1], addr[2], addr[3]);
if (handleServer(connfd) < 0) {
closesocket(connfd);
break;
}
closesocket(connfd);
}
exit:;
printf("DK: exited thread\n");
if (servfd >= 0) {
closesocket(servfd);
servfd = -1;
}
// close smd
smdIopClose(DK_SMD_IDX_READ);
smdIopClose(DK_SMD_IDX_WRITE);
threadRunning = 0;
return 0;
}
static void dkPCHARDeactivateSmd() {
// close server socket and tell thread to stop
if (servfd >= 0) {
closesocket(servfd);
servfd = -1;
}
threadRunning = 0;
// wait for thread to return
IOS_JoinThread(threadId, NULL);
}
int dkPCHARActivateSmd(IOSVec *vecs) {
int32_t ret = 0;
DKPCHARResult *result = (DKPCHARResult *) vecs[0].ptr;
DKPCHARActivateParams *params = (DKPCHARActivateParams *) vecs[1].ptr;
printf("PCHAR Activate (%lx) for %s: SMDs %s %s\n", params->command, params->path, params->smdReadName, params->smdWriteName);
// Prevent starting another thread while the old one is still running
if (threadRunning) {
printf("DK: thread is already running, stopping...\n");
dkPCHARDeactivateSmd();
}
ret = smdIopInit(DK_SMD_IDX_WRITE, (SmdControlTable *) vecs[2].ptr, params->smdWriteName, 0);
if (ret != 0) {
goto done;
}
ret = smdIopInit(DK_SMD_IDX_READ, (SmdControlTable *) vecs[3].ptr, params->smdReadName, 0);
if (ret != 0) {
goto done;
}
ret = smdIopOpen(DK_SMD_IDX_WRITE);
if (ret != 0) {
goto done;
}
ret = smdIopOpen(DK_SMD_IDX_READ);
if (ret != 0) {
goto done;
}
threadId = svcCreateThread(dkThread, 0, (u32 *) (threadStack + sizeof(threadStack)), sizeof(threadStack), 0x77, 0);
if (threadId < 0) {
ret = threadId;
goto done;
}
if (svcStartThread(threadId) < 0) {
printf("DK: failed to start thread\n");
ret = -11;
}
done:;
printf("PCHAR Activate %lx\n", ret);
result->error = ret;
return ret;
}

View File

@ -0,0 +1,47 @@
#pragma once
#include "ipc_types.h"
#include <assert.h>
#include <stdint.h>
#ifndef PACKED
#define PACKED __attribute__((__packed__))
#endif
#ifndef CHECK_SIZE
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, #type " must be " #size " bytes")
#endif
enum DKPCHARCommand {
DK_PCHAR_COMMAND_READ = 0x40,
DK_PCHAR_COMMAND_WRITE = 0x41,
};
typedef struct PACKED {
uint32_t command;
uint8_t __unk[0x8];
char path[0x20];
uint8_t __padding[0x4];
char smdWriteName[0x10];
char smdReadName[0x10];
} DKPCHARActivateParams;
CHECK_SIZE(DKPCHARActivateParams, 0x50);
typedef struct PACKED {
int32_t error;
uint32_t param0;
uint32_t param1;
} DKPCHARResult;
CHECK_SIZE(DKPCHARResult, 0xC);
typedef struct PACKED {
uint32_t command;
void *message;
uint32_t unk;
void *buf;
uint32_t size;
uint8_t __padding[0x3C];
} DKPCHARResponse;
CHECK_SIZE(DKPCHARResponse, 0x50);
int dkPCHARActivateSmd(IOSVec *vecs);

View File

@ -1,25 +1,5 @@
#include "imports.h"
void usleep(u32 time) {
((void (*const)(u32)) 0x050564E4)(time);
}
void *memset(void *dst, int val, size_t size) {
char *_dst = dst;
int i;
for (i = 0; i < size; i++)
_dst[i] = val;
return dst;
}
void *(*const _memcpy)(void *dst, void *src, int size) = (void *) 0x05054E54;
void *memcpy(void *dst, const void *src, size_t size) {
return _memcpy(dst, (void *) src, size);
}
int strlen(const char *str) {
unsigned int i = 0;
while (str[i]) {
@ -28,19 +8,6 @@ int strlen(const char *str) {
return i;
}
int strncmp(const char *s1, const char *s2, size_t n) {
while (n && *s1 && (*s1 == *s2)) {
++s1;
++s2;
--n;
}
if (n == 0) {
return 0;
} else {
return (*(unsigned char *) s1 - *(unsigned char *) s2);
}
}
// Function to implement strncat() function in C
char *strncat(char *destination, const char *source, size_t num) {
// make ptr point to the end of destination string
@ -56,18 +23,3 @@ char *strncat(char *destination, const char *source, size_t num) {
// destination string is returned by standard strncat()
return destination;
}
char *strncpy(char *dst, const char *src, size_t size) {
int i;
for (i = 0; i < size; i++) {
dst[i] = src[i];
if (src[i] == '\0')
return dst;
}
return dst;
}
int vsnprintf(char *s, size_t n, const char *format, va_list arg) {
return ((int (*const)(char *, size_t, const char *, va_list)) 0x05055C40)(s, n, format, arg);
}

View File

@ -7,6 +7,32 @@
#define MCP_SVC_BASE ((void *) 0x050567EC)
void usleep(u32 time);
typedef enum {
MEM_PERM_R = 1 << 0,
MEM_PERM_W = 1 << 1,
MEM_PERM_RW = (MEM_PERM_R | MEM_PERM_W),
} MemPermFlags;
int IOS_JoinThread(int threadid, int32_t *returned_value);
int IOS_CreateMessageQueue(uint32_t *ptr, uint32_t n_msgs);
int IOS_DestroyMessageQueue(int queueid);
int IOS_SendMessage(int queueid, uint32_t message, uint32_t flags);
int IOS_ReceiveMessage(int queueid, uint32_t *message, uint32_t flags);
int IOS_CreateTimer(uint32_t time_us, uint32_t repeat_time_us, int queueid, uint32_t message);
int IOS_RestartTimer(int timerid, uint32_t time_us, uint32_t repeat_time_us);
int IOS_DestroyTimer(int timerid);
void IOS_InvalidateDCache(void *ptr, uint32_t len);
void IOS_FlushDCache(void *ptr, uint32_t len);
int32_t IOS_CheckIOPAddressRange(void *ptr, uint32_t size, MemPermFlags perm);
uint32_t IOS_VirtToPhys(void *ptr);
int IOS_CreateSemaphore(int32_t maxCount, int32_t initialCount);
int IOS_WaitSemaphore(int id, uint32_t tryWait);
int IOS_SignalSemaphore(int id);
int IOS_DestroySemaphore(int id);
#endif

View File

@ -22,6 +22,7 @@
* distribution.
***************************************************************************/
#include "../../common/kernel_commands.h"
#include "dk.h"
#include "fsa.h"
#include "imports.h"
#include "logger.h"
@ -74,6 +75,8 @@
#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)));
@ -384,6 +387,21 @@ static int ipc_ioctl(ipcmessage *message) {
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 *arg) {
int res;
ipcmessage *message;
@ -417,7 +435,7 @@ static int ipc_thread(void *arg) {
}
case IOS_IOCTLV: {
log_printf("IOS_IOCTLV\n");
res = 0;
res = ipc_ioctlv(message);
break;
}
default: {

View File

@ -19,6 +19,11 @@
#define IOS_RESUME 0x0D
#define IOS_SVCMSG 0x0E
typedef struct {
void *ptr;
uint32_t len;
uint32_t paddr;
} IOSVec;
/* IPC message */
typedef struct ipcmessage {
@ -61,9 +66,9 @@ typedef struct ipcmessage {
struct _ioctlv {
u32 command;
u32 num_in;
u32 num_io;
struct _ioctlv *vector;
uint32_t num_in;
uint32_t num_out;
IOSVec *vecs;
} ioctlv;
};

View File

@ -1,5 +1,4 @@
#include "ipc.h"
#include "wupserver.h"
static int threadsStarted = 0;

View File

@ -79,9 +79,9 @@ int _MCP_LoadFile_patch(ipcmessage *msg) {
if (strncmp(request->name, "men.rpx", strlen("men.rpx")) == 0) {
rpxpath[0] = '\0';
if (skipPPCSetup) {
snprintf(rpxpath, sizeof(rpxpath) - 1, "%s/men.rpx", &((char *) 0x05119F00)[19]); // Copy in environment path
snprintf(rpxpath, sizeof(rpxpath) - 1, "%s/men.rpx", &((char *) 0x0511FF00)[19]); // Copy in environment path
} else {
snprintf(rpxpath, sizeof(rpxpath) - 1, "%s/root.rpx", &((char *) 0x05119F00)[19]); // Copy in environment path
snprintf(rpxpath, sizeof(rpxpath) - 1, "%s/root.rpx", &((char *) 0x0511FF00)[19]); // Copy in environment path
}
// At startup we want to hook into the Wii U Menu by replacing the men.rpx with a file from the SD Card
@ -296,7 +296,7 @@ int _MCP_ioctl100_patch(ipcmessage *msg) {
}
case IPC_CUSTOM_COPY_ENVIRONMENT_PATH: {
if (msg->ioctl.buffer_io && msg->ioctl.length_io >= 0x100) {
strncpy((char *) msg->ioctl.buffer_io, (void *) 0x05119F00, 0xFF);
strncpy((char *) msg->ioctl.buffer_io, (void *) 0x0511FF00, 0xFF);
return 0;
} else {
return 29;

320
source/ios_mcp/source/smd.c Normal file
View File

@ -0,0 +1,320 @@
#include "smd.h"
#include <imports.h>
#include <string.h>
static SmdIopContext contexts[8];
static void *smdPhysToVirt(SmdIopContext *ctx, uint32_t paddr) {
return (void *) (paddr + ctx->memVirtOffset);
}
static uint32_t smdVirtToPhys(SmdIopContext *ctx, void *vaddr) {
return (uint32_t) vaddr + ctx->memPhysOffset;
}
int32_t smdIopInit(int32_t index, SmdControlTable *table, const char *name, int lock) {
if (index > 7) {
return -0xc0003;
}
SmdIopContext *ctx = &contexts[index];
// Verify table pointer
if (IOS_CheckIOPAddressRange(table, sizeof(SmdControlTable), MEM_PERM_RW) != 0) {
return -0xc0007;
}
ctx->controlTable = table;
if (lock && !ctx->hasSemaphore) {
ctx->semaphore = IOS_CreateSemaphore(1, 1);
if (ctx->semaphore < 0) {
return -0xc0008;
}
ctx->hasSemaphore = 1;
}
ctx->shouldLock = lock;
IOS_InvalidateDCache(table, sizeof(SmdControlTable));
if (strncmp(table->name, name, sizeof(table->name)) != 0) {
return -0xc0007;
}
// Calculate virt and phys offsets
uint32_t paddr = IOS_VirtToPhys(table);
ctx->memVirtOffset = (int32_t) table - paddr;
ctx->memPhysOffset = paddr - (int32_t) table;
ctx->iopElementBuf = (SmdElement *) smdPhysToVirt(ctx, table->iopInterface.bufPaddr);
ctx->iopElementCount = table->iopInterface.elementCount;
ctx->ppcElementBuf = (SmdElement *) smdPhysToVirt(ctx, table->ppcInterface.bufPaddr);
ctx->ppcElementCount = table->ppcInterface.elementCount;
// Verify element bufffers
if (IOS_CheckIOPAddressRange(ctx->iopElementBuf, ctx->iopElementCount * sizeof(SmdElement), MEM_PERM_RW) != 0 ||
IOS_CheckIOPAddressRange(ctx->ppcElementBuf, ctx->ppcElementCount * sizeof(SmdElement), MEM_PERM_RW) != 0) {
return -0xc0007;
}
ctx->state = SMD_STATE_INITIALIZED;
return 0;
}
int32_t smdIopOpen(int32_t index) {
if (index > 7) {
return -0xc0003;
}
SmdIopContext *ctx = &contexts[index];
if (ctx->state < SMD_STATE_INITIALIZED) {
return -0xc000d;
}
// Update interface state
ctx->controlTable->iopInterface.state = SMD_INTERFACE_STATE_OPENED;
IOS_FlushDCache(&ctx->controlTable->iopInterface.state, 4);
ctx->state = SMD_STATE_OPENED;
return 0;
}
int32_t smdIopClose(int32_t index) {
if (index > 7) {
return -0xc0003;
}
SmdIopContext *ctx = &contexts[index];
if (ctx->state < SMD_STATE_INITIALIZED) {
return -0xc000d;
}
// Update interface state
ctx->controlTable->iopInterface.state = SMD_INTERFACE_STATE_CLOSED;
IOS_FlushDCache(&ctx->controlTable->iopInterface.state, 4);
ctx->state = SMD_STATE_CLOSED;
return 0;
}
int32_t smdIopGetInterfaceState(int32_t index, SmdInterfaceState *iopState, SmdInterfaceState *ppcState) {
if (index > 7) {
return -0xc0003;
}
SmdIopContext *ctx = &contexts[index];
if (ctx->state < SMD_STATE_INITIALIZED) {
return -0xc000d;
}
// Read interface states
if (ppcState) {
IOS_InvalidateDCache(&ctx->controlTable->ppcInterface.state, 4);
*ppcState = (SmdInterfaceState) ctx->controlTable->ppcInterface.state;
}
if (iopState) {
IOS_InvalidateDCache(&ctx->controlTable->iopInterface.state, 4);
*iopState = (SmdInterfaceState) ctx->controlTable->iopInterface.state;
}
return 0;
}
int32_t smdIopReceive(int32_t index, SmdReceiveData *data) {
int32_t ret = 0;
int32_t incoming;
if (index > 7) {
return -0xc0003;
}
SmdIopContext *ctx = &contexts[index];
SmdInterface *iface = &ctx->controlTable->iopInterface;
memset(data, 0, sizeof(SmdReceiveData));
if (ctx->shouldLock) {
IOS_WaitSemaphore(ctx->semaphore, 0);
}
IOS_InvalidateDCache(iface, sizeof(SmdInterface));
if (iface->readOffset > (int32_t) ctx->iopElementCount) {
ret = -0xc0007;
goto done;
}
// figure out how many incoming elements we can receive
if (iface->readOffset < iface->writeOffset) {
incoming = iface->writeOffset - iface->readOffset;
} else if (iface->writeOffset < iface->readOffset) {
incoming = iface->elementCount - iface->readOffset;
if (iface->writeOffset > 0) {
incoming += iface->writeOffset;
}
} else {
ret = -0xc0005;
goto done;
}
if (incoming <= 0) {
ret = -0xc0005;
goto done;
}
SmdElement *element = &ctx->iopElementBuf[iface->readOffset];
IOS_InvalidateDCache(element, sizeof(SmdElement));
data->size = element->size;
data->type = element->type;
// handle message types
if (element->type == SMD_ELEMENT_TYPE_MESSAGE) {
if (element->size > 0x80) {
ret = -0xc0001;
goto done;
}
memcpy(data->message, element->data, element->size);
} else if (element->type == SMD_ELEMENT_TYPE_VECTOR_SPEC) {
SmdVector *vec = &element->spec;
data->spec.count = vec->count;
data->spec.id = vec->id;
if (data->spec.count > 4) {
ret = -0xc0002;
goto done;
}
// Translate and verify spec pointers
for (int i = 0; i < data->spec.count; ++i) {
SmdVectorSpec *spec = &data->spec.vecs[i];
spec->ptr = smdPhysToVirt(ctx, (uint32_t) vec->vecs[i].ptr);
spec->size = vec->vecs[i].size;
if (IOS_CheckIOPAddressRange(spec->ptr, spec->size, MEM_PERM_RW) != 0) {
ret = -0xc000f;
goto done;
}
IOS_InvalidateDCache(spec->ptr, spec->size);
}
} else if (element->type == SMD_ELEMENT_TYPE_VECTOR) {
SmdVector *vec = (SmdVector *) smdPhysToVirt(ctx, element->vectorPaddr);
IOS_InvalidateDCache(vec, sizeof(SmdVector));
if (vec->count > 4) {
ret = -0xc0002;
goto done;
}
data->vector = vec;
// Translate and verify pointers
for (int i = 0; i < vec->count; ++i) {
SmdVectorSpec *spec = &data->vector->vecs[i];
spec->ptr = smdPhysToVirt(ctx, (uint32_t) spec->ptr);
if (IOS_CheckIOPAddressRange(spec->ptr, spec->size, MEM_PERM_RW) != 0) {
ret = -0xc000f;
goto done;
}
IOS_InvalidateDCache(spec->ptr, spec->size);
}
} else {
ret = -0xc0006;
goto done;
}
// Increment read offset
iface->readOffset = (iface->readOffset + 1) % iface->elementCount;
IOS_FlushDCache(&iface->readOffset, 4);
done:;
if (ctx->shouldLock) {
IOS_SignalSemaphore(ctx->semaphore);
}
return ret;
}
static int32_t writeElement(SmdIopContext *ctx, SmdElementType type, void *data, uint32_t size) {
int32_t ret = 0;
int32_t outgoing;
if (ctx->shouldLock) {
IOS_WaitSemaphore(ctx->semaphore, 0);
}
SmdInterface *iface = &ctx->controlTable->ppcInterface;
IOS_InvalidateDCache(iface, sizeof(SmdInterface));
if (iface->writeOffset > (int32_t) ctx->ppcElementCount) {
ret = -0xc0007;
goto done;
}
// figure out how many outgoing messages we can send
if (iface->writeOffset < iface->readOffset) {
outgoing = iface->readOffset - iface->writeOffset;
} else {
outgoing = iface->elementCount - iface->writeOffset;
if (iface->readOffset >= 0) {
outgoing += iface->readOffset;
}
}
if (outgoing <= 0) {
goto done;
}
// write data to element
SmdElement *element = &ctx->ppcElementBuf[iface->writeOffset];
element->type = type;
element->size = size;
memcpy(element->data, data, size);
IOS_FlushDCache(element, sizeof(SmdElement));
// Increment write offset
iface->writeOffset = (iface->writeOffset + 1) % iface->elementCount;
IOS_FlushDCache(&iface->writeOffset, 4);
done:;
if (ctx->shouldLock) {
IOS_SignalSemaphore(ctx->semaphore);
}
return ret;
}
int32_t smdIopSendVector(int32_t index, SmdVector *vector) {
if (index > 7) {
return -0xc0003;
}
SmdIopContext *ctx = &contexts[index];
if (vector->count > 4) {
return -0xc0002;
}
// flush and translate pointers
for (int i = 0; i < vector->count; ++i) {
SmdVectorSpec *spec = &vector->vecs[i];
IOS_FlushDCache(spec->ptr, spec->size);
spec->ptr = (void *) smdVirtToPhys(ctx, spec->ptr);
}
IOS_FlushDCache(vector, sizeof(SmdVector));
// write vector paddr as element
uint32_t paddr = smdVirtToPhys(ctx, vector);
return writeElement(ctx, SMD_ELEMENT_TYPE_VECTOR, &paddr, sizeof(paddr));
}

128
source/ios_mcp/source/smd.h Normal file
View File

@ -0,0 +1,128 @@
#pragma once
#include <assert.h>
#include <stdint.h>
#ifndef PACKED
#define PACKED __attribute__((__packed__))
#endif
#ifndef CHECK_SIZE
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, #type " must be " #size " bytes")
#endif
typedef enum SmdState SmdState;
typedef enum SmdInterfaceState SmdInterfaceState;
typedef enum SmdElementType SmdElementType;
typedef struct SmdIopContext SmdIopContext;
typedef struct SmdInterface SmdInterface;
typedef struct SmdControlTable SmdControlTable;
typedef struct SmdVectorSpec SmdVectorSpec;
typedef struct SmdVector SmdVector;
typedef struct SmdElement SmdElement;
typedef struct SmdReceiveData SmdReceiveData;
enum SmdState {
SMD_STATE_INVALID = 0,
SMD_STATE_INITIALIZED = 2,
SMD_STATE_CLOSED = 3,
SMD_STATE_OPENED = 4,
};
enum SmdInterfaceState {
SMD_INTERFACE_STATE_OPENED = 0x2222,
SMD_INTERFACE_STATE_CLOSED = 0x3333,
};
enum SmdElementType {
SMD_ELEMENT_TYPE_MESSAGE = 0,
SMD_ELEMENT_TYPE_VECTOR_SPEC = 1,
SMD_ELEMENT_TYPE_VECTOR = 2,
};
struct SmdIopContext {
SmdControlTable *controlTable;
int32_t memVirtOffset;
int32_t memPhysOffset;
int shouldLock;
int semaphore;
int hasSemaphore;
int logHandle;
SmdElement *iopElementBuf;
uint32_t iopElementCount;
SmdElement *ppcElementBuf;
uint32_t ppcElementCount;
SmdState state;
};
struct PACKED SmdInterface {
uint32_t state;
uint8_t __padding0[0x7C];
uint32_t elementCount;
uint8_t __padding1[0x7C];
int32_t readOffset;
uint8_t __padding2[0x7C];
int32_t writeOffset;
uint8_t __padding3[0x7C];
uint32_t bufPaddr;
uint8_t __padding4[0x7C];
};
CHECK_SIZE(SmdInterface, 0x280);
struct PACKED SmdControlTable {
char name[0x10];
uint32_t reinitCount;
uint8_t __padding0[0x6C];
SmdInterface iopInterface;
uint8_t __padding1[0x40];
SmdInterface ppcInterface;
uint8_t __padding2[0x40];
};
CHECK_SIZE(SmdControlTable, 0x600);
struct PACKED SmdVectorSpec {
void *ptr;
uint32_t size;
};
CHECK_SIZE(SmdVectorSpec, 0x8);
struct PACKED SmdVector {
uint32_t id;
int32_t count;
SmdVectorSpec vecs[4];
};
CHECK_SIZE(SmdVector, 0x28);
struct PACKED SmdElement {
uint32_t type;
uint32_t size;
union {
uint8_t data[0xf8];
SmdVector spec;
uint32_t vectorPaddr;
};
};
CHECK_SIZE(SmdElement, 0x100);
struct PACKED SmdReceiveData {
uint32_t type;
uint32_t size;
union {
uint8_t message[0x80];
SmdVector spec;
SmdVector *vector;
};
};
CHECK_SIZE(SmdReceiveData, 0x88);
int32_t smdIopInit(int32_t index, SmdControlTable *table, const char *name, int lock);
int32_t smdIopOpen(int32_t index);
int32_t smdIopClose(int32_t index);
int32_t smdIopGetInterfaceState(int32_t index, SmdInterfaceState *iopState, SmdInterfaceState *ppcState);
int32_t smdIopReceive(int32_t index, SmdReceiveData *data);
int32_t smdIopSendVector(int32_t index, SmdVector *vector);

View File

@ -209,3 +209,27 @@ int send(int sockfd, const void *buf, size_t len, int flags) {
freeIobuf(iobuf);
return ret;
}
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {
void *data_buf = svcAllocAlign(0xCAFF, optlen, 0x40);
if (!data_buf) return -100;
u8 *iobuf = allocIobuf(sizeof(IOSVec) * 2 + sizeof(uint32_t) * 3);
iovec_s *iovec = (iovec_s *) iobuf;
memcpy(data_buf, optval, optlen);
iovec[0].ptr = data_buf;
iovec[0].len = optlen;
iovec[1].ptr = iovec;
iovec[1].len = sizeof(IOSVec) * 3;
((uint32_t *) iovec)[6] = sockfd;
((uint32_t *) iovec)[7] = level;
((uint32_t *) iovec)[8] = optname;
int ret = svcIoctlv(socket_handle, 0x9, 2, 0, iovec);
freeIobuf(iobuf);
freeIobuf(data_buf);
return ret;
}

View File

@ -5,7 +5,7 @@
#include <stdint.h>
#include <stdio.h>
#define SOL_SOCKET 0xFFFF
#define SOL_SOCKET -1
#define PF_UNSPEC 0
#define PF_INET 2
@ -30,24 +30,29 @@
#define SHUT_WR 1
#define SHUT_RDWR 2
#define SO_DEBUG 0x0001
#define SO_ACCEPTCONN 0x0002
#define SO_REUSEADDR 0x0004
#define SO_KEEPALIVE 0x0008
#define SO_DONTROUTE 0x0010
#define SO_BROADCAST 0x0020
#define SO_USELOOPBACK 0x0040
#define SO_LINGER 0x0080
#define SO_OOBINLINE 0x0100
#define SO_REUSEPORT 0x0200
#define SO_SNDBUF 0x1001
#define SO_RCVBUF 0x1002
#define SO_SNDLOWAT 0x1003
#define SO_RCVLOWAT 0x1004
#define SO_SNDTIMEO 0x1005
#define SO_RCVTIMEO 0x1006
#define SO_ERROR 0x1007
#define SO_TYPE 0x1008
/*
* SOL_SOCKET options
*/
#define SO_REUSEADDR 0x0004 // reuse address
#define SO_KEEPALIVE 0x0008 // keep connections alive
#define SO_BROADCAST 0x0020 // broadcast
#define SO_LINGER 0x0080 // linger (no effect?)
#define SO_OOBINLINE 0x0100 // out-of-band data inline (no effect?)
#define SO_TCPSACK 0x0200 // set tcp selective acknowledgment
#define SO_WINSCALE 0x0400 // set tcp window scaling
#define SO_SNDBUF 0x1001 // send buffer size
#define SO_RCVBUF 0x1002 // receive buffer size
#define SO_SNDLOWAT 0x1003 // send low-water mark (no effect?)
#define SO_RCVLOWAT 0x1004 // receive low-water mark
#define SO_TYPE 0x1008 // get socket type
#define SO_ERROR 0x1009 // get socket error
#define SO_RXDATA 0x1011 // get count of bytes in sb_rcv
#define SO_TXDATA 0x1012 // get count of bytes in sb_snd
#define SO_NBIO 0x1014 // set socket to NON-blocking mode
#define SO_BIO 0x1015 // set socket to blocking mode
#define SO_NONBLOCK 0x1016 // set/get blocking mode via optval param
#define TCP_NODELAY 0x2004
#define INADDR_ANY 0x00000000
#define INADDR_BROADCAST 0xFFFFFFFF