Integrate 3ds_pxi and 3ds_sm

This commit is contained in:
TuxSH
2017-11-02 15:11:55 +01:00
parent 76dde0e6db
commit 8258a98647
37 changed files with 2806 additions and 1 deletions

View File

@@ -0,0 +1,51 @@
/*
common.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds.h>
#include "memory.h"
#define IS_PRE_7X (osGetFirmVersion() < SYSTEM_VERSION(2, 39, 4))
#define IS_PRE_93 (osGetFirmVersion() < SYSTEM_VERSION(2, 48, 3))
extern u32 nbSection0Modules;
extern Handle resumeGetServiceHandleOrPortRegisteredSemaphore;
struct SessionDataList;
typedef struct SessionData
{
struct SessionData *prev, *next;
struct SessionDataList *parent;
u32 pid;
u32 replayCmdbuf[4];
Handle busyClientPortHandle;
Handle handle;
bool isSrvPm;
} SessionData;
typedef struct SessionDataList
{
SessionData *first, *last;
} SessionDataList;
extern SessionDataList sessionDataInUseList, freeSessionDataList;
extern SessionDataList sessionDataWaitingForServiceOrPortRegisterList, sessionDataToWakeUpAfterServiceOrPortRegisterList;
extern SessionDataList sessionDataWaitingPortReadyList;
static inline void panic(void)
{
svcBreak(USERBREAK_PANIC);
for(;;) svcSleepThread(0);
}
static inline void assertSuccess(Result res)
{
if(R_FAILED(res))
panic();
}

106
sysmodules/sm/source/list.c Normal file
View File

@@ -0,0 +1,106 @@
/*
list.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "list.h"
#include "memory.h"
#include "common.h"
struct ListBase;
typedef struct ListNodeBase
{
struct ListNodeBase *prev, *next;
struct ListBase *parent;
} ListNodeBase;
typedef struct ListBase
{
ListNodeBase *first, *last;
} ListBase;
void buildList(void *list, void *pool, u32 nb, u32 elementSize)
{
ListBase *listB = (ListBase *)list;
for(u32 i = 0; i < nb; i++)
{
ListNodeBase *node = (ListNodeBase *)((u8 *)pool + i * elementSize);
node->prev = i == 0 ? NULL : (ListNodeBase *)((u8 *)pool + (i - 1) * elementSize);
node->next = i == (nb - 1) ? NULL : (ListNodeBase *)((u8 *)pool + (i + 1) * elementSize);
node->parent = list;
}
listB->first = (ListNodeBase *)pool;
listB->last = (ListNodeBase *)((u8 *)pool + (nb - 1) * elementSize);
}
void moveNode(void *node, void *dst, bool back)
{
ListNodeBase *nodeB = (ListNodeBase *)node;
ListBase *dstB = (ListBase *)dst;
ListBase *srcB = nodeB->parent;
if(dstB == srcB)
return;
// Remove the node in the source list
if(nodeB->prev != NULL)
nodeB->prev->next = nodeB->next;
if(nodeB->next != NULL)
nodeB->next->prev = nodeB->prev;
// Update the source list if needed
if(nodeB == srcB->first)
srcB->first = nodeB->next;
if(nodeB == srcB->last)
srcB->last = nodeB->prev;
// Insert the node in the destination list
if(back)
{
if(dstB->last != NULL)
dstB->last->next = nodeB;
nodeB->prev = dstB->last;
nodeB->next = NULL;
dstB->last = nodeB;
}
else
{
if(dstB->first != NULL)
dstB->first->prev = nodeB;
nodeB->next = dstB->first;
nodeB->prev = NULL;
dstB->first = nodeB;
}
// Normalize the destination list
if(dstB->first != NULL && dstB->last == NULL)
dstB->last = dstB->first;
else if(dstB->first == NULL && dstB->last != NULL)
dstB->first = dstB->last;
nodeB->parent = dstB;
}
void *allocateNode(void *inUseList, void *freeList, u32 elementSize, bool back)
{
ListBase *freeListB = (ListBase *)freeList;
if(freeListB->first == NULL)
panic();
ListNodeBase *node = freeListB->first;
ListNodeBase nodeBk = *node;
memset(node, 0, elementSize);
*node = nodeBk;
moveNode(node, inUseList, back);
return node;
}

View File

@@ -0,0 +1,14 @@
/*
list.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds/types.h>
void buildList(void *list, void *pool, u32 nb, u32 elementSize);
void moveNode(void *node, void *dst, bool back);
void *allocateNode(void *inUseList, void *freeList, u32 elementSize, bool back);

299
sysmodules/sm/source/main.c Normal file
View File

@@ -0,0 +1,299 @@
/*
main.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "common.h"
#include "memory.h"
#include "services.h"
#include "processes.h"
#include "srv.h"
#include "srv_pm.h"
#include "list.h"
extern u32 __ctru_heap;
extern u32 __ctru_linear_heap;
extern char (*serviceAccessListBuffers)[34][8];
u32 __ctru_heap_size = 0x4000;
u32 __ctru_linear_heap_size = 0;
u32 nbSection0Modules;
Handle resumeGetServiceHandleOrPortRegisteredSemaphore;
SessionDataList sessionDataInUseList = {NULL, NULL}, freeSessionDataList = {NULL, NULL};
SessionDataList sessionDataWaitingForServiceOrPortRegisterList = {NULL, NULL}, sessionDataToWakeUpAfterServiceOrPortRegisterList = {NULL, NULL};
SessionDataList sessionDataWaitingPortReadyList = {NULL, NULL};
static SessionData sessionDataPool[76];
static ProcessData processDataPool[64];
static u8 ALIGN(4) serviceAccessListStaticBuffer[0x110];
void __appInit(void)
{
s64 out;
u32 *staticBuffers = getThreadStaticBuffers();
staticBuffers[0] = IPC_Desc_StaticBuffer(0x110, 0);
staticBuffers[1] = (u32)serviceAccessListStaticBuffer;
svcGetSystemInfo(&out, 26, 0);
nbSection0Modules = out;
assertSuccess(svcCreateSemaphore(&resumeGetServiceHandleOrPortRegisteredSemaphore, 0, 64));
serviceAccessListBuffers = (char (*)[34][8])__ctru_heap;
buildList(&freeSessionDataList, sessionDataPool, sizeof(sessionDataPool) / sizeof(SessionData), sizeof(SessionData));
buildList(&freeProcessDataList, processDataPool, sizeof(processDataPool) / sizeof(ProcessData), sizeof(ProcessData));
}
// this is called after main exits
void __appExit(void){}
void __system_allocateHeaps(void)
{
u32 tmp = 0;
// Allocate the application heap
__ctru_heap = 0x08000000;
svcControlMemory(&tmp, __ctru_heap, 0x0, __ctru_heap_size, MEMOP_ALLOC, MEMPERM_READ | MEMPERM_WRITE);
__ctru_linear_heap = 0;
}
void __system_initSyscalls(void){}
Result __sync_init(void);
Result __sync_fini(void);
void __ctru_exit(void){}
void initSystem(void)
{
__sync_init();
__system_allocateHeaps();
__appInit();
}
int main(void)
{
Result res;
u32 *cmdbuf = getThreadCommandBuffer();
u32 nbHandles = 3, nbSessions = 0, nbSrvPmSessions = 0;
Handle clientPortDummy;
Handle srvPort, srvPmPort;
Handle handles[0xE3] = { 0 };
Handle replyTarget = 0;
u32 smPid;
SessionData *sessionData;
assertSuccess(svcGetProcessId(&smPid, CUR_PROCESS_HANDLE));
assertSuccess(svcCreatePort(&srvPort, &clientPortDummy, "srv:", 64));
if(IS_PRE_7X)
assertSuccess(svcCreatePort(&srvPmPort, &clientPortDummy, "srv:pm", 64));
else
assertSuccess(doRegisterService(smPid, &srvPmPort, "srv:pm", 6, 64));
handles[0] = resumeGetServiceHandleOrPortRegisteredSemaphore;
handles[1] = srvPort;
handles[2] = srvPmPort;
for(;;)
{
s32 id;
if(replyTarget == 0)
cmdbuf[0] = 0xFFFF0000; // Kernel11
res = svcReplyAndReceive(&id, handles, nbHandles, replyTarget);
if(res == (Result)0xC920181A) // unreachable remote
{
// Note: if a process has ended, pm will call UnregisterProcess on it
if(id < 0)
{
for(id = 3; (u32)id < nbHandles && handles[id] != replyTarget; id++);
if((u32)id >= nbHandles)
panic();
}
if(id < 3)
panic();
else if((u32)id < 3 + nbSessions) // Session closed
{
sessionData = NULL;
for(u32 i = 0; i < sizeof(sessionDataPool) / sizeof(SessionData); i++)
{
if(sessionDataPool[i].handle == handles[id])
sessionData = &sessionDataPool[i];
}
if(sessionData != NULL)
{
if(sessionData->busyClientPortHandle != 0) // remove unreferenced client port handles from array
{
u32 refCount = 0;
for(u32 i = 0; i < sizeof(sessionDataPool) / sizeof(SessionData); i++)
{
if(sessionDataPool[i].busyClientPortHandle == sessionData->busyClientPortHandle)
++refCount;
}
if(refCount <= 1)
{
u32 i;
for(i = 3 + nbSessions; i < nbHandles && handles[i] == sessionData->busyClientPortHandle; i++);
if(i < nbHandles)
handles[i] = handles[--nbHandles];
}
}
--nbHandles;
for(u32 i = (u32)id; i < nbHandles; i++)
handles[i] = handles[i + 1];
--nbSessions;
svcCloseHandle(sessionData->handle);
if(sessionData->isSrvPm)
--nbSrvPmSessions;
moveNode(sessionData, &freeSessionDataList, false);
}
else
panic();
}
else // Port closed
{
SessionData *nextSessionData = NULL;
// Update the command postponing reason accordingly
for(sessionData = sessionDataWaitingPortReadyList.first; sessionData != NULL; sessionData = nextSessionData)
{
nextSessionData = sessionData->next;
if(sessionData->busyClientPortHandle == handles[id])
{
sessionData->replayCmdbuf[1] = 0xD0406401; // unregistered service or named port
moveNode(sessionData, &sessionDataWaitingForServiceOrPortRegisterList, true);
svcCloseHandle(handles[id]);
handles[id] = handles[--nbHandles];
sessionData->busyClientPortHandle = 0;
}
}
}
replyTarget = 0;
}
else if(R_FAILED(res))
panic();
else
{
replyTarget = 0;
if(id == 1) // New srv: session
{
Handle session;
assertSuccess(svcAcceptSession(&session, srvPort));
sessionData = (SessionData *)allocateNode(&sessionDataInUseList, &freeSessionDataList, sizeof(SessionData), false);
sessionData->pid = (u32)-1;
sessionData->handle = session;
for(u32 i = nbHandles; i > 3 + nbSessions; i--)
handles[i] = handles[i - 1];
handles[3 + nbSessions++] = session;
++nbHandles;
}
else if(id == 2) // New srv:pm session
{
Handle session;
if(!IS_PRE_7X && nbSrvPmSessions >= 1)
panic();
assertSuccess(svcAcceptSession(&session, srvPmPort));
sessionData = (SessionData *)allocateNode(&sessionDataInUseList, &freeSessionDataList, sizeof(SessionData), false);
sessionData->pid = (u32)-1;
sessionData->handle = session;
sessionData->isSrvPm = true;
for(u32 i = nbHandles; i > 3 + nbSessions; i--)
handles[i] = handles[i - 1];
handles[3 + nbSessions++] = session;
++nbHandles;
++nbSrvPmSessions;
}
else
{
if(id == 0) // Resume SRV:GetServiceHandle or GetPort due to service or named port not registered
{
if(sessionDataToWakeUpAfterServiceOrPortRegisterList.first == NULL)
panic();
sessionData = sessionDataToWakeUpAfterServiceOrPortRegisterList.first;
moveNode(sessionData, &sessionDataInUseList, false);
for(u32 i = nbHandles; i > 3 + nbSessions; i--)
handles[i] = handles[i - 1];
handles[3 + nbSessions++] = sessionData->handle;
++nbHandles;
if(sessionData->isSrvPm)
++nbSrvPmSessions;
memcpy(cmdbuf, sessionData->replayCmdbuf, 16);
}
else if((u32)id >= 3 + nbSessions) // Resume SRV:GetServiceHandle if service was full
{
SessionData *sTmp;
for(sessionData = sessionDataWaitingPortReadyList.first; sessionData != NULL && sessionData->busyClientPortHandle != handles[id];
sessionData = sessionData->next);
if(sessionData == NULL)
panic();
moveNode(sessionData, &sessionDataInUseList, false);
for(sTmp = sessionDataWaitingPortReadyList.first; sTmp != NULL && sTmp->busyClientPortHandle != handles[id]; sTmp = sTmp->next);
if(sTmp == NULL)
handles[id] = handles[--nbHandles];
for(u32 i = nbHandles + 1; i > 3 + nbSessions; i--)
handles[i] = handles[i - 1];
handles[3 + nbSessions++] = sessionData->handle;
++nbHandles;
if(sessionData->isSrvPm)
++nbSrvPmSessions;
memcpy(cmdbuf, sessionData->replayCmdbuf, 16);
sessionData->busyClientPortHandle = 0;
}
else
{
for(sessionData = sessionDataInUseList.first; sessionData != NULL && sessionData->handle != handles[id]; sessionData = sessionData->next);
if(sessionData == NULL)
panic();
}
for(id = 3; (u32)id < 3 + nbSessions && handles[id] != sessionData->handle; id++);
if((u32)id >= 3 + nbSessions)
panic();
res = sessionData->isSrvPm ? srvPmHandleCommands(sessionData) : srvHandleCommands(sessionData);
if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK)
{
SessionDataList *dstList = NULL;
if(res == (Result)0xD0406401) // service or named port not registered yet
dstList = &sessionDataWaitingForServiceOrPortRegisterList;
else if(res == (Result)0xD0406402) // service full
{
u32 i;
dstList = &sessionDataWaitingPortReadyList;
for(i = 3 + nbSessions; i < nbHandles && handles[i] != sessionData->busyClientPortHandle; i++);
if(i >= nbHandles)
handles[nbHandles++] = sessionData->busyClientPortHandle;
}
else
panic();
--nbHandles;
for(u32 i = (u32)id; i < nbHandles; i++)
handles[i] = handles[i + 1];
--nbSessions;
if(sessionData->isSrvPm)
--nbSrvPmSessions;
moveNode(sessionData, dstList, true);
}
else
replyTarget = sessionData->handle;
}
}
}
return 0;
}

View File

@@ -0,0 +1,27 @@
/*
memory.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "memory.h"
/*
//Adpated from CakesFW
void memcpy(void *dest, const void *src, u32 size)
{
u8 *destc = (u8 *)dest;
const u8 *srcc = (const u8 *)src;
for(u32 i = 0; i < size; i++)
destc[i] = srcc[i];
}*/
s32 strnlen(const char *string, s32 maxlen)
{
s32 size;
for(size = 0; size < maxlen && *string; string++, size++);
return size;
}

View File

@@ -0,0 +1,18 @@
/*
memory.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds/types.h>
//void memcpy(void *dest, const void *src, u32 size);
#define memcpy __builtin_memcpy
#define memset __builtin_memset
#define strncmp __builtin_strncmp
#define strncpy __builtin_strncpy
s32 strnlen(const char *string, s32 maxlen);

View File

@@ -0,0 +1,191 @@
/*
notifications.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "notifications.h"
#include "processes.h"
static bool doPublishNotification(ProcessData *processData, u32 notificationId, u32 flags)
{
if((flags & 1) && processData->nbPendingNotifications != 0) // only send if not already pending
{
for(u16 i = 0; i < processData->nbPendingNotifications; i++)
{
if(processData->pendingNotifications[(processData->pendingNotificationIndex + i) % 16] == notificationId)
return true;
}
}
if(processData->nbPendingNotifications < 0x10)
{
s32 count;
processData->pendingNotifications[processData->pendingNotificationIndex] = notificationId;
processData->pendingNotificationIndex = (processData->pendingNotificationIndex + 1) % 16;
++processData->nbPendingNotifications;
assertSuccess(svcReleaseSemaphore(&count, processData->notificationSemaphore, 1));
return true;
}
else
return (flags & 2) != 0;
}
Result EnableNotification(SessionData *sessionData, Handle *notificationSemaphore)
{
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL)
{
// Section 0 modules have access to all services, so we need to register them here for notifications
if(sessionData->pid < nbSection0Modules)
processData = doRegisterProcess(sessionData->pid, NULL, 0);
else
return 0xD8806404;
}
processData->notificationEnabled = true;
*notificationSemaphore = processData->notificationSemaphore;
return 0;
}
Result Subscribe(SessionData *sessionData, u32 notificationId)
{
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL || !processData->notificationEnabled)
return 0xD8806404;
for(u16 i = 0; i < processData->nbSubscribed; i++)
{
if(processData->subscribedNotifications[i] == notificationId)
return 0xD9006403;
}
if(processData->nbSubscribed < 0x11)
{
processData->subscribedNotifications[processData->nbSubscribed++] = notificationId;
return 0;
}
else
return 0xD9006405;
}
Result Unsubscribe(SessionData *sessionData, u32 notificationId)
{
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL || !processData->notificationEnabled)
return 0xD8806404;
u16 i;
for(i = 0; i < processData->nbSubscribed && processData->subscribedNotifications[i] != notificationId; i++);
if(i == processData->nbSubscribed)
return 0xD8806404;
else
{
processData->subscribedNotifications[i] = processData->subscribedNotifications[--processData->nbSubscribed];
return 0;
}
}
Result ReceiveNotification(SessionData *sessionData, u32 *notificationId)
{
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL || !processData->notificationEnabled || processData->nbPendingNotifications == 0)
{
if(processData->nbPendingNotifications)
*notificationId = 0;
return 0xD8806404;
}
else
{
--processData->nbPendingNotifications;
*notificationId = processData->pendingNotifications[processData->receivedNotificationIndex];
processData->receivedNotificationIndex = (processData->receivedNotificationIndex + 1) % 16;
return 0;
}
}
Result PublishToSubscriber(u32 notificationId, u32 flags)
{
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
{
if(!node->notificationEnabled)
continue;
u16 i;
for(i = 0; i < node->nbSubscribed && node->subscribedNotifications[i] != notificationId; i++);
if(i >= node->nbSubscribed)
continue;
if(!doPublishNotification(node, notificationId, flags))
return 0xD8606408;
}
return 0;
}
Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags)
{
u32 nb = 0;
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
{
if(!node->notificationEnabled)
continue;
u16 i;
for(i = 0; i < node->nbSubscribed && node->subscribedNotifications[i] != notificationId; i++);
if(i >= node->nbSubscribed)
continue;
if(!doPublishNotification(node, notificationId, flags))
return 0xD8606408;
else if(pidList != NULL && nb < 60)
pidList[nb++] = node->pid;
}
if(pidCount != NULL)
*pidCount = nb;
return 0;
}
Result PublishToProcess(Handle process, u32 notificationId)
{
u32 pid;
Result res = svcGetProcessId(&pid, process);
if(R_FAILED(res))
return res;
ProcessData *processData = findProcessData(pid);
if(processData == NULL || !processData->notificationEnabled)
res = 0xD8806404;
else if(!doPublishNotification(processData, notificationId, 0))
res = 0xD8606408;
else
res = 0;
svcCloseHandle(process);
return res;
}
Result PublishToAll(u32 notificationId)
{
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
{
if(!node->notificationEnabled)
continue;
else if(!doPublishNotification(node, notificationId, 0))
return 0xD8606408;
}
return 0;
}

View File

@@ -0,0 +1,19 @@
/*
notifications.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
Result EnableNotification(SessionData *sessionData, Handle *notificationSemaphore);
Result Subscribe(SessionData *sessionData, u32 notificationId);
Result Unsubscribe(SessionData *sessionData, u32 notificationId);
Result ReceiveNotification(SessionData *sessionData, u32 *notificationId);
Result PublishToSubscriber(u32 notificationId, u32 flags);
Result PublishAndGetSubscriber(u32 *pidCount, u32 *pidList, u32 notificationId, u32 flags);
Result PublishToProcess(Handle process, u32 notificationId);
Result PublishToAll(u32 notificationId);

View File

@@ -0,0 +1,88 @@
/*
processes.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "list.h"
#include "processes.h"
#include "services.h"
ProcessDataList processDataInUseList = { NULL, NULL }, freeProcessDataList = { NULL, NULL };
char (*serviceAccessListBuffers)[34][8];
// The kernel limits the number of processes to 47 anyways...
static u64 freeServiceAccessListBuffersIds = (1ULL << 59) - 1;
ProcessData *findProcessData(u32 pid)
{
for(ProcessData *node = processDataInUseList.first; node != NULL; node = node->next)
{
if(node->pid == pid)
return node;
}
return NULL;
}
ProcessData *doRegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize)
{
ProcessData *processData = (ProcessData *)allocateNode(&processDataInUseList, &freeProcessDataList, sizeof(ProcessData), false);
if(serviceAccessListSize != 0)
{
s32 bufferId = 63 - __builtin_clzll(freeServiceAccessListBuffersIds);
if(bufferId == -1)
panic();
else
{
freeServiceAccessListBuffersIds &= ~(1ULL << bufferId);
processData->serviceAccessList = serviceAccessListBuffers[bufferId];
processData->serviceAccessListSize = serviceAccessListSize;
memcpy(processData->serviceAccessList, serviceAccessList, serviceAccessListSize);
}
}
assertSuccess(svcCreateSemaphore(&processData->notificationSemaphore, 0, 0x10));
processData->pid = pid;
return processData;
}
Result RegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize)
{
serviceAccessListSize = serviceAccessListSize - (serviceAccessListSize % 8);
if(findProcessData(pid) != NULL)
return 0xD9006403;
else if(serviceAccessListSize > 8 * (IS_PRE_93 ? 32 : 34))
return 0xD9006405;
else
{
doRegisterProcess(pid, serviceAccessList, serviceAccessListSize);
return 0;
}
}
Result UnregisterProcess(u32 pid)
{
ProcessData *processData = findProcessData(pid);
if(processData == NULL)
return 0xD8806404;
svcCloseHandle(processData->notificationSemaphore);
// Unregister the services registered by the process
for(u32 i = 0; i < nbServices; i++)
{
if(servicesInfo[i].pid == pid)
{
svcCloseHandle(servicesInfo[i].clientPort);
servicesInfo[i] = servicesInfo[--nbServices];
}
}
freeServiceAccessListBuffersIds |= 1ULL << (u32)((processData->serviceAccessList - serviceAccessListBuffers[0]) / 34);
moveNode(processData, &freeProcessDataList, false);
return 0;
}

View File

@@ -0,0 +1,48 @@
/*
processes.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
struct ProcessDataList;
typedef struct ProcessData
{
struct ProcessData *prev, *next;
struct ProcessDataList *parent;
u32 pid;
Handle notificationSemaphore;
char (*serviceAccessList)[8];
u32 serviceAccessListSize;
bool notificationEnabled;
// Circular buffer
u16 receivedNotificationIndex;
u16 pendingNotificationIndex;
u16 nbPendingNotifications;
u32 pendingNotifications[16];
u16 nbSubscribed;
u32 subscribedNotifications[17];
} ProcessData;
typedef struct ProcessDataList
{
ProcessData *first, *last;
} ProcessDataList;
extern ProcessDataList processDataInUseList, freeProcessDataList;
ProcessData *findProcessData(u32 pid);
ProcessData *doRegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize);
Result RegisterProcess(u32 pid, char (*serviceAccessList)[8], u32 serviceAccessListSize);
Result UnregisterProcess(u32 pid);

View File

@@ -0,0 +1,217 @@
/*
services.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "services.h"
#include "processes.h"
#include "memory.h"
#include "list.h"
ServiceInfo servicesInfo[0xA0] = { 0 };
u32 nbServices = 0; // including "ports" registered with getPort
static Result checkServiceName(const char *name, s32 nameSize)
{
if(nameSize <= 0 || nameSize > 8)
return 0xD9006405;
else if(strnlen(name, nameSize) < nameSize)
return 0xD9006407;
else
return 0;
}
static inline bool areServiceNamesEqual(const char *name, const char *name2, s32 nameSize)
{
return strncmp(name, name2, nameSize) == 0 && (nameSize == 8 || name[nameSize] == 0);
}
static s32 findServicePortByName(bool isNamedPort, const char *name, s32 nameSize)
{
ServiceInfo *info;
for(info = servicesInfo; info < servicesInfo + nbServices && (info->isNamedPort != isNamedPort || !areServiceNamesEqual(info->name, name, nameSize)); info++);
return info >= servicesInfo + nbServices ? -1 : info - servicesInfo;
}
static bool checkServiceAccess(SessionData *sessionData, const char *name, s32 nameSize)
{
if(sessionData->pid < nbSection0Modules)
return true;
ProcessData *processData = findProcessData(sessionData->pid);
if(processData == NULL)
return false;
for(u32 i = 0; i < processData->serviceAccessListSize; i++)
{
if(areServiceNamesEqual(processData->serviceAccessList[i], name, nameSize))
return true;
}
return false;
}
static Result doRegisterServiceOrPort(u32 pid, Handle *serverPort, Handle clientPort, const char *name, s32 nameSize, s32 maxSessions, bool isNamedPort)
{
Result res = checkServiceName(name, nameSize);
Handle portServer, portClient;
if(R_FAILED(res))
return res;
else if(findServicePortByName(isNamedPort, name, nameSize) != -1)
return 0xD9001BFC;
if(nbServices >= 0xA0)
return 0xD86067F3;
if(!isNamedPort)
{
res = svcCreatePort(&portServer, &portClient, NULL, maxSessions);
if(R_FAILED(res))
return 0xD9001BFC;
}
else
portClient = clientPort;
ServiceInfo *serviceInfo = &servicesInfo[nbServices++];
strncpy(serviceInfo->name, name, 8);
serviceInfo->pid = pid;
serviceInfo->clientPort = portClient;
serviceInfo->isNamedPort = isNamedPort;
SessionData *nextSessionData;
s32 n = 0;
for(SessionData *node = sessionDataWaitingForServiceOrPortRegisterList.first; node != NULL; node = nextSessionData)
{
nextSessionData = node->next;
if((node->replayCmdbuf[0] & 0xF0000) == (!isNamedPort ? 0x50000 : 0x80000) &&
areServiceNamesEqual((const char *)(node->replayCmdbuf + 1), name, (s32)node->replayCmdbuf[3]))
{
moveNode(node, &sessionDataToWakeUpAfterServiceOrPortRegisterList, true);
++n;
}
}
if(n > 0)
{
s32 count;
assertSuccess(svcReleaseSemaphore(&count, resumeGetServiceHandleOrPortRegisteredSemaphore, n));
}
if(!isNamedPort)
*serverPort = portServer;
return res;
}
Result doRegisterService(u32 pid, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions)
{
return doRegisterServiceOrPort(pid, serverPort, 0, name, nameSize, maxSessions, false);
}
Result RegisterService(SessionData *sessionData, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions)
{
return doRegisterService(sessionData->pid, serverPort, name, nameSize, maxSessions);
}
Result RegisterPort(SessionData *sessionData, Handle clientPort, const char *name, s32 nameSize)
{
return doRegisterServiceOrPort(sessionData->pid, NULL, clientPort, name, nameSize, -1, true);
}
static Result UnregisterServiceOrPort(SessionData *sessionData, const char *name, s32 nameSize, bool isNamedPort)
{
Result res = checkServiceName(name, nameSize);
s32 serviceId;
if(R_FAILED(res))
return res;
serviceId = findServicePortByName(isNamedPort, name, nameSize);
if(serviceId == -1)
return 0xD8801BFA;
else if(servicesInfo[serviceId].pid != sessionData->pid)
return 0xD8E06406;
else
{
svcCloseHandle(servicesInfo[serviceId].clientPort);
servicesInfo[serviceId] = servicesInfo[--nbServices];
return 0;
}
}
Result UnregisterService(SessionData *sessionData, const char *name, s32 nameSize)
{
return UnregisterServiceOrPort(sessionData, name, nameSize, false);
}
Result UnregisterPort(SessionData *sessionData, const char *name, s32 nameSize)
{
return UnregisterServiceOrPort(sessionData, name, nameSize, true);
}
Result IsServiceRegistered(SessionData *sessionData, bool *isRegistered, const char *name, s32 nameSize)
{
Result res = checkServiceName(name, nameSize);
if(R_FAILED(res))
return res;
else if(!checkServiceAccess(sessionData, name, nameSize))
return 0xD8E06406;
else
{
*isRegistered = findServicePortByName(false, name, nameSize) != -1;
return 0;
}
}
Result GetServiceHandle(SessionData *sessionData, Handle *session, const char *name, s32 nameSize, u32 flags)
{
Result res = checkServiceName(name, nameSize);
s32 serviceId;
if(R_FAILED(res))
return res;
else if(!checkServiceAccess(sessionData, name, nameSize))
return 0xD8E06406;
serviceId = findServicePortByName(false, name, nameSize);
if(serviceId == -1)
return 0xD0406401;
else
{
Handle port = servicesInfo[serviceId].clientPort;
res = svcCreateSessionToPort(session, port);
if(res == (Result)0xD0401834 && !(flags & 1))
{
sessionData->busyClientPortHandle = port;
return 0xD0406402;
}
return 0;
}
}
Result GetPort(SessionData *sessionData, Handle *port, const char *name, s32 nameSize, u8 flags)
{
Result res = checkServiceName(name, nameSize);
s32 serviceId;
if(R_FAILED(res))
return res;
else if(!checkServiceAccess(sessionData, name, nameSize))
return 0xD8E06406;
serviceId = findServicePortByName(true, name, nameSize);
if(serviceId == -1)
return flags != 0 ? 0xD0406401 : 0xD8801BFA;
else if(flags == 0)
*port = servicesInfo[serviceId].clientPort;
return 0;
}

View File

@@ -0,0 +1,32 @@
/*
services.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include <3ds.h>
#include "common.h"
typedef struct ServiceInfo
{
char name[8];
Handle clientPort;
u32 pid;
bool isNamedPort;
} ServiceInfo;
extern ServiceInfo servicesInfo[0xA0];
extern u32 nbServices;
Result doRegisterService(u32 pid, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions);
Result RegisterService(SessionData *sessionData, Handle *serverPort, const char *name, s32 nameSize, s32 maxSessions);
Result RegisterPort(SessionData *sessionData, Handle clientPort, const char *name, s32 nameSize);
Result UnregisterService(SessionData *sessionData, const char *name, s32 nameSize);
Result UnregisterPort(SessionData *sessionData, const char *name, s32 nameSize);
Result IsServiceRegistered(SessionData *SessionData, bool *isRegistered, const char *name, s32 nameSize);
Result GetServiceHandle(SessionData *sessionData, Handle *session, const char *name, s32 nameSize, u32 flags);
Result GetPort(SessionData *sessionData, Handle *port, const char *name, s32 nameSize, u8 flags);

181
sysmodules/sm/source/srv.c Normal file
View File

@@ -0,0 +1,181 @@
/*
srv.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "srv.h"
#include "services.h"
#include "notifications.h"
#include "processes.h"
Result srvHandleCommands(SessionData *sessionData)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
switch(cmdbuf[0] >> 16)
{
case 1: // RegisterClient
{
if(cmdbuf[0] == IPC_MakeHeader(1, 0, 2) && cmdbuf[1] == IPC_Desc_CurProcessHandle())
{
sessionData->pid = cmdbuf[2];
cmdbuf[0] = IPC_MakeHeader(1, 1, 0);
cmdbuf[1] = 0;
}
else
goto invalid_command;
break;
}
case 2: // EnableNotification
{
Handle notificationSemaphore = 0;
res = EnableNotification(sessionData, &notificationSemaphore);
cmdbuf[0] = IPC_MakeHeader(2, 1, 2);
cmdbuf[1] = (u32)res;
cmdbuf[2] = IPC_Desc_SharedHandles(1);
cmdbuf[3] = notificationSemaphore;
break;
}
case 3: // RegisterService
{
Handle serverPort = 0;
res = RegisterService(sessionData, &serverPort, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], (s32)cmdbuf[4]);
cmdbuf[0] = IPC_MakeHeader(3, 1, 2);
cmdbuf[1] = (u32)res;
cmdbuf[2] = IPC_Desc_MoveHandles(1);
cmdbuf[3] = serverPort;
break;
}
case 4: // UnregisterService
{
res = UnregisterService(sessionData, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]);
cmdbuf[0] = IPC_MakeHeader(4, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 5: // GetServiceHandle
{
Handle session = 0;
res = GetServiceHandle(sessionData, &session, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], cmdbuf[4]);
if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK)
memcpy(sessionData->replayCmdbuf, cmdbuf, 16);
else
{
cmdbuf[0] = IPC_MakeHeader(5, 1, 2);
cmdbuf[1] = (u32)res;
cmdbuf[2] = IPC_Desc_MoveHandles(1);
cmdbuf[3] = session;
}
break;
}
case 6: // RegisterPort
{
if(cmdbuf[0] == IPC_MakeHeader(6, 3, 2) && cmdbuf[4] == IPC_Desc_SharedHandles(1))
{
res = RegisterPort(sessionData, (Handle)cmdbuf[5], (const char *)(cmdbuf + 1), (s32)cmdbuf[3]);
cmdbuf[0] = IPC_MakeHeader(6, 1, 0);
cmdbuf[1] = (u32)res;
}
else
goto invalid_command;
break;
}
case 7: // UnregisterPort
{
res = UnregisterPort(sessionData, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]);
cmdbuf[0] = IPC_MakeHeader(7, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 8: // GetPort
{
Handle port = 0;
res = GetPort(sessionData, &port, (const char *)(cmdbuf + 1), (s32)cmdbuf[3], (u8)cmdbuf[4]);
if(R_MODULE(res) == RM_SRV && R_SUMMARY(res) == RS_WOULDBLOCK)
memcpy(sessionData->replayCmdbuf, cmdbuf, 16);
else
{
cmdbuf[0] = IPC_MakeHeader(8, 1, 2);
cmdbuf[1] = (u32)res;
cmdbuf[2] = IPC_Desc_SharedHandles(1);
cmdbuf[3] = port;
}
break;
}
case 9: // Subscribe
{
res = Subscribe(sessionData, cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(9, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 10: // Unsubscribe
{
res = Unsubscribe(sessionData, cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(10, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 11: // ReceiveNotification
{
u32 notificationId;
res = ReceiveNotification(sessionData, &notificationId);
cmdbuf[0] = IPC_MakeHeader(11, 2, 0);;
cmdbuf[1] = (u32)res;
cmdbuf[2] = notificationId;
break;
}
case 12: // PublishToSubscriber
{
res = PublishToSubscriber(cmdbuf[1], cmdbuf[2]);
cmdbuf[0] = IPC_MakeHeader(12, 1, 0);;
cmdbuf[1] = (u32)res;
break;
}
case 13: // PublishAndGetSubscriber
{
u32 pidCount;
res = PublishAndGetSubscriber(&pidCount, cmdbuf + 3, cmdbuf[1], 0);
cmdbuf[0] = IPC_MakeHeader(13, 62, 0);
cmdbuf[1] = (u32)res;
cmdbuf[2] = pidCount;
break;
}
case 14: // IsServiceRegistered
{
bool isRegistered;
res = IsServiceRegistered(sessionData, &isRegistered, (const char *)(cmdbuf + 1), (s32)cmdbuf[3]);
cmdbuf[0] = IPC_MakeHeader(14, 2, 0);;
cmdbuf[1] = (u32)res;
cmdbuf[2] = isRegistered ? 1 : 0;
break;
}
default:
goto invalid_command;
break;
}
return res;
invalid_command:
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);;
cmdbuf[1] = (u32)0xD9001830;
return 0xD9001830;
}

View File

@@ -0,0 +1,12 @@
/*
srv.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
Result srvHandleCommands(SessionData *sessionData);

View File

@@ -0,0 +1,81 @@
/*
srv_pm.c
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#include "srv_pm.h"
#include "srv.h"
#include "services.h"
#include "notifications.h"
#include "processes.h"
Result srvPmHandleCommands(SessionData *sessionData)
{
Result res = 0;
u32 *cmdbuf = getThreadCommandBuffer();
u32 mask = (cmdbuf[0] & 0x04000000) >> 16;
if(IS_PRE_7X && mask == 0)
return srvHandleCommands(sessionData);
else if(!IS_PRE_7X && mask != 0)
goto invalid_command;
switch((cmdbuf[0] >> 16) & ~mask)
{
case 1: // PublishToProcess
{
if(cmdbuf[0] == IPC_MakeHeader(mask | 1, 1, 2) && cmdbuf[2] == IPC_Desc_SharedHandles(1))
{
res = PublishToProcess((Handle)cmdbuf[3], cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(mask | 1, 1, 0);
cmdbuf[1] = (u32)res;
}
else
goto invalid_command;
break;
}
case 2: // PublishToAll
{
res = PublishToAll(cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(mask | 2, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
case 3: // RegisterProcess
{
if(cmdbuf[0] == IPC_MakeHeader(mask | 3, 2, 2) && (cmdbuf[3] & 0x3C0F) == (IPC_Desc_StaticBuffer(0x110, 0) & 0x3C0F))
{
res = RegisterProcess(cmdbuf[1], (char (*)[8])cmdbuf[4], cmdbuf[3] >> 14);
cmdbuf[0] = IPC_MakeHeader(mask | 3, 1, 0);
cmdbuf[1] = (u32)res;
}
else
goto invalid_command;
break;
}
case 4:
{
res = UnregisterProcess(cmdbuf[1]);
cmdbuf[0] = IPC_MakeHeader(mask | 4, 1, 0);
cmdbuf[1] = (u32)res;
break;
}
default:
goto invalid_command;
break;
}
return res;
invalid_command:
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);;
cmdbuf[1] = (u32)0xD900182F;
return 0xD900182F;
}

View File

@@ -0,0 +1,12 @@
/*
srv_pm.h
(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/
#pragma once
#include "common.h"
Result srvPmHandleCommands(SessionData *sessionData);