232 lines
8.4 KiB
C
232 lines
8.4 KiB
C
/*
|
|
main.c
|
|
|
|
(c) TuxSH, 2017-2020
|
|
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
|
|
*/
|
|
|
|
#include "common.h"
|
|
#include "services.h"
|
|
#include "processes.h"
|
|
#include "srv.h"
|
|
#include "srv_pm.h"
|
|
#include "list.h"
|
|
|
|
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 __ctru_exit(int rc) { (void)rc; } // needed to avoid linking error
|
|
|
|
// this is called after main exits
|
|
void __wrap_exit(int rc)
|
|
{
|
|
(void)rc;
|
|
// Not supposed to terminate... kernel will clean up the handles if it does happen anyway
|
|
svcExitProcess();
|
|
}
|
|
|
|
void __sync_init();
|
|
// void __libc_init_array(void);
|
|
|
|
// Called before main
|
|
void initSystem(void)
|
|
{
|
|
__sync_init();
|
|
|
|
//__libc_init_array();
|
|
|
|
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));
|
|
|
|
buildList(&freeSessionDataList, sessionDataPool, sizeof(sessionDataPool) / sizeof(SessionData), sizeof(SessionData));
|
|
buildList(&freeProcessDataList, processDataPool, sizeof(processDataPool) / sizeof(ProcessData), sizeof(ProcessData));
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
Result res;
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
u32 nbHandles = 3, nbSessions = 0;
|
|
bool srvPmSessionCreated = false;
|
|
|
|
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 = -1;
|
|
if(replyTarget == 0)
|
|
cmdbuf[0] = 0xFFFF0000; // Kernel11
|
|
|
|
// Rebuild the list
|
|
nbHandles = 3;
|
|
nbSessions = 0;
|
|
for(sessionData = sessionDataInUseList.first; sessionData != NULL; sessionData = sessionData->next)
|
|
{
|
|
handles[nbHandles++] = sessionData->handle;
|
|
nbSessions++;
|
|
}
|
|
|
|
for(sessionData = sessionDataWaitingPortReadyList.first; sessionData != NULL; sessionData = sessionData->next)
|
|
{
|
|
handles[nbHandles++] = sessionData->busyClientPortHandle;
|
|
}
|
|
|
|
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 = 0; (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)
|
|
{
|
|
svcCloseHandle(sessionData->handle);
|
|
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);
|
|
sessionData->busyClientPortHandle = 0;
|
|
}
|
|
}
|
|
|
|
// Don't close the handle.
|
|
}
|
|
|
|
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;
|
|
}
|
|
else if(id == 2) // New srv:pm session
|
|
{
|
|
Handle session;
|
|
if(!IS_PRE_7X && srvPmSessionCreated)
|
|
panic();
|
|
assertSuccess(svcAcceptSession(&session, srvPmPort));
|
|
sessionData = (SessionData *)allocateNode(&sessionDataInUseList, &freeSessionDataList, sizeof(SessionData), false);
|
|
sessionData->pid = (u32)-1;
|
|
sessionData->handle = session;
|
|
sessionData->isSrvPm = true;
|
|
}
|
|
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);
|
|
memcpy(cmdbuf, sessionData->replayCmdbuf, 16);
|
|
}
|
|
else if((u32)id >= 3 + nbSessions) // Resume SRV:GetServiceHandle if service was full
|
|
{
|
|
for(sessionData = sessionDataWaitingPortReadyList.first; sessionData != NULL && sessionData->busyClientPortHandle != handles[id];
|
|
sessionData = sessionData->next);
|
|
if(sessionData == NULL) {
|
|
// Ignore (client process could have ended) and continue
|
|
continue;
|
|
}
|
|
moveNode(sessionData, &sessionDataInUseList, false);
|
|
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();
|
|
}
|
|
|
|
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
|
|
dstList = &sessionDataWaitingPortReadyList;
|
|
else
|
|
panic();
|
|
|
|
moveNode(sessionData, dstList, true);
|
|
}
|
|
else
|
|
replyTarget = sessionData->handle;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|