diff --git a/sysmodules/rosalina/include/errdisp.h b/sysmodules/rosalina/include/errdisp.h
index ed97e99..33e69bc 100644
--- a/sysmodules/rosalina/include/errdisp.h
+++ b/sysmodules/rosalina/include/errdisp.h
@@ -27,8 +27,5 @@
#pragma once
#include <3ds/types.h>
-#include "MyThread.h"
-MyThread *errDispCreateThread(void);
-
-void errDispThreadMain(void);
+void ERRF_HandleCommands(void *ctx);
diff --git a/sysmodules/rosalina/include/hbloader.h b/sysmodules/rosalina/include/hbloader.h
index 4dbf1d2..18af0a6 100644
--- a/sysmodules/rosalina/include/hbloader.h
+++ b/sysmodules/rosalina/include/hbloader.h
@@ -33,6 +33,4 @@
#define HBLDR_DEFAULT_3DSX_TID 0x000400000D921E00ULL
#define HBLDR_3DSX_TID (*(vu64 *)0x1FF81100)
-MyThread *hbldrCreateThread(void);
-
-void hbldrThreadMain(void);
+void HBLDR_HandleCommands(void *ctx);
diff --git a/sysmodules/rosalina/include/service_manager.h b/sysmodules/rosalina/include/service_manager.h
new file mode 100644
index 0000000..fca20c1
--- /dev/null
+++ b/sysmodules/rosalina/include/service_manager.h
@@ -0,0 +1,48 @@
+/*
+* This file is part of Luma3DS
+* Copyright (C) 2016-2019 Aurora Wright, TuxSH
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+* * Requiring preservation of specified reasonable legal notices or
+* author attributions in that material or in the Appropriate Legal
+* Notices displayed by works containing it.
+* * Prohibiting misrepresentation of the origin of that material,
+* or requiring that modified versions of such material be marked in
+* reasonable ways as different from the original version.
+*/
+
+#pragma once
+
+#include <3ds/types.h>
+
+typedef struct ServiceManagerServiceEntry {
+ const char *name;
+ u32 maxSessions;
+ void (*handler)(void *ctx);
+ bool isGlobalPort;
+} ServiceManagerServiceEntry;
+
+typedef struct ServiceManagerNotificationEntry {
+ u32 id;
+ void (*handler)(u32 id);
+} ServiceManagerNotificationEntry;
+
+typedef struct ServiceManagerContextAllocator {
+ void* (*newSessionContext)(u8 serviceId);
+ void (*freeSessionContext)(void *ctx);
+} ServiceManagerContextAllocator;
+
+Result ServiceManager_Run(const ServiceManagerServiceEntry *services, const ServiceManagerNotificationEntry *notifications, const ServiceManagerContextAllocator *allocator);
diff --git a/sysmodules/rosalina/source/errdisp.c b/sysmodules/rosalina/source/errdisp.c
index 290cd86..62cacb5 100644
--- a/sysmodules/rosalina/source/errdisp.c
+++ b/sysmodules/rosalina/source/errdisp.c
@@ -38,18 +38,8 @@ static inline void assertSuccess(Result res)
svcBreak(USERBREAK_PANIC);
}
-static MyThread errDispThread;
-static u8 ALIGN(8) errDispThreadStack[0x2000];
-
static char userString[0x100 + 1] = {0};
-MyThread *errDispCreateThread(void)
-{
- if(R_FAILED(MyThread_Create(&errDispThread, errDispThreadMain, errDispThreadStack, 0x2000, 0x18, CORE_SYSTEM)))
- svcBreak(USERBREAK_PANIC);
- return &errDispThread;
-}
-
static inline u32 ERRF_DisplayRegisterValue(u32 posX, u32 posY, const char *name, u32 value)
{
return Draw_DrawFormattedString(posX, posY, COLOR_WHITE, "%-9s %08lx", name, value);
@@ -235,8 +225,9 @@ static Result ERRF_SaveErrorToFile(ERRF_FatalErrInfo *info)
return res;
}
-static void ERRF_HandleCommands(void)
+void ERRF_HandleCommands(void *ctx)
{
+ (void)ctx;
u32 *cmdbuf = getThreadCommandBuffer();
switch(cmdbuf[0] >> 16)
@@ -289,64 +280,3 @@ static void ERRF_HandleCommands(void)
}
}
}
-
-void errDispThreadMain(void)
-{
- Handle handles[2];
- Handle serverHandle, clientHandle, sessionHandle = 0;
-
- u32 replyTarget = 0;
- s32 index;
-
- Result res;
- u32 *cmdbuf = getThreadCommandBuffer();
-
- assertSuccess(svcCreatePort(&serverHandle, &clientHandle, "err:f", 1));
-
- do
- {
- handles[0] = serverHandle;
- handles[1] = sessionHandle;
-
- if(replyTarget == 0) // k11
- cmdbuf[0] = 0xFFFF0000;
- res = svcReplyAndReceive(&index, handles, sessionHandle == 0 ? 1 : 2, replyTarget);
-
- if(R_FAILED(res))
- {
- if((u32)res == 0xC920181A) // session closed by remote
- {
- svcCloseHandle(sessionHandle);
- sessionHandle = 0;
- replyTarget = 0;
- }
-
- else
- svcBreak(USERBREAK_PANIC);
- }
-
- else
- {
- if(index == 0)
- {
- Handle session;
- assertSuccess(svcAcceptSession(&session, serverHandle));
-
- if(sessionHandle == 0)
- sessionHandle = session;
- else
- svcCloseHandle(session);
- }
- else
- {
- ERRF_HandleCommands();
- replyTarget = sessionHandle;
- }
- }
- }
- while(!terminationRequest);
-
- svcCloseHandle(sessionHandle);
- svcCloseHandle(clientHandle);
- svcCloseHandle(serverHandle);
-}
diff --git a/sysmodules/rosalina/source/hbloader.c b/sysmodules/rosalina/source/hbloader.c
index 986882c..39173ab 100644
--- a/sysmodules/rosalina/source/hbloader.c
+++ b/sysmodules/rosalina/source/hbloader.c
@@ -122,16 +122,8 @@ static inline void assertSuccess(Result res)
svcBreak(USERBREAK_PANIC);
}
-static MyThread hbldrThread;
-static u8 ALIGN(8) hbldrThreadStack[THREAD_STACK_SIZE];
static u16 hbldrTarget[PATH_MAX+1];
-MyThread *hbldrCreateThread(void)
-{
- assertSuccess(MyThread_Create(&hbldrThread, hbldrThreadMain, hbldrThreadStack, THREAD_STACK_SIZE, 0x18, CORE_SYSTEM));
- return &hbldrThread;
-}
-
static inline void error(u32* cmdbuf, Result rc)
{
cmdbuf[0] = IPC_MakeHeader(0, 1, 0);
@@ -149,8 +141,9 @@ static u16 *u16_strncpy(u16 *dest, const u16 *src, u32 size)
return dest;
}
-static void HBLDR_HandleCommands(void)
+void HBLDR_HandleCommands(void *ctx)
{
+ (void)ctx;
Result res;
IFile file;
u32 *cmdbuf = getThreadCommandBuffer();
@@ -347,72 +340,3 @@ static void HBLDR_HandleCommands(void)
}
}
}
-
-void hbldrThreadMain(void)
-{
- Handle handles[2];
- Handle serverHandle, clientHandle, sessionHandle = 0;
-
- u32 replyTarget = 0;
- s32 index;
-
- char ipcBuf[PATH_MAX+1];
- u32* bufPtrs = getThreadStaticBuffers();
- memset(bufPtrs, 0, 16*2*4);
- bufPtrs[0] = IPC_Desc_StaticBuffer(sizeof(ipcBuf), 0);
- bufPtrs[1] = (u32)ipcBuf;
- bufPtrs[2] = IPC_Desc_StaticBuffer(sizeof(ldrArgvBuf), 1);
- bufPtrs[3] = (u32)ldrArgvBuf;
-
- Result res;
- u32 *cmdbuf = getThreadCommandBuffer();
-
- assertSuccess(svcCreatePort(&serverHandle, &clientHandle, "hb:ldr", 1));
-
- do
- {
- handles[0] = serverHandle;
- handles[1] = sessionHandle;
-
- if(replyTarget == 0) // k11
- cmdbuf[0] = 0xFFFF0000;
- res = svcReplyAndReceive(&index, handles, sessionHandle == 0 ? 1 : 2, replyTarget);
-
- if(R_FAILED(res))
- {
- if((u32)res == 0xC920181A) // session closed by remote
- {
- svcCloseHandle(sessionHandle);
- sessionHandle = 0;
- replyTarget = 0;
- }
-
- else
- svcBreak(USERBREAK_PANIC);
- }
-
- else
- {
- if(index == 0)
- {
- Handle session;
- assertSuccess(svcAcceptSession(&session, serverHandle));
-
- if(sessionHandle == 0)
- sessionHandle = session;
- else
- svcCloseHandle(session);
- }
- else
- {
- HBLDR_HandleCommands();
- replyTarget = sessionHandle;
- }
- }
- }
- while(!terminationRequest);
-
- svcCloseHandle(sessionHandle);
- svcCloseHandle(clientHandle);
- svcCloseHandle(serverHandle);
-}
diff --git a/sysmodules/rosalina/source/main.c b/sysmodules/rosalina/source/main.c
index cf37952..dc54a1a 100644
--- a/sysmodules/rosalina/source/main.c
+++ b/sysmodules/rosalina/source/main.c
@@ -29,8 +29,10 @@
#include "services.h"
#include "fsreg.h"
#include "menu.h"
+#include "service_manager.h"
#include "errdisp.h"
#include "hbloader.h"
+#include "3dsx.h"
#include "utils.h"
#include "MyThread.h"
#include "menus/process_patches.h"
@@ -67,7 +69,6 @@ void __ctru_exit()
__libc_fini_array();
__appExit();
__sync_fini();
- for(;;) svcSleepThread(0); // kernel-loaded sysmodules except PXI are not supposed to terminate anyways
svcExitProcess();
}
@@ -105,46 +106,48 @@ void initSystem()
bool terminationRequest = false;
Handle terminationRequestEvent;
+static void handleTermNotification(u32 notificationId)
+{
+ (void)notificationId;
+ // Termination request
+ terminationRequest = true;
+ svcSignalEvent(terminationRequestEvent);
+}
+
+static const ServiceManagerServiceEntry services[] = {
+ { "err:f", 1, ERRF_HandleCommands, true },
+ { "hb:ldr", 2, HBLDR_HandleCommands, true },
+ { NULL },
+};
+
+static const ServiceManagerNotificationEntry notifications[] = {
+ { 0x100, handleTermNotification },
+ { 0x000, NULL },
+};
+
int main(void)
{
- Result res = 0;
- Handle notificationHandle;
+ static u8 ipcBuf[0x100] = {0}; // used by both err:f and hb:ldr
- if(R_FAILED(srvEnableNotification(¬ificationHandle)))
- svcBreak(USERBREAK_ASSERT);
+ // Set up static buffers for IPC
+ u32* bufPtrs = getThreadStaticBuffers();
+ memset(bufPtrs, 0, 16 * 2 * 4);
+ bufPtrs[0] = IPC_Desc_StaticBuffer(sizeof(ipcBuf), 0);
+ bufPtrs[1] = (u32)ipcBuf;
+ bufPtrs[2] = IPC_Desc_StaticBuffer(sizeof(ldrArgvBuf), 1);
+ bufPtrs[3] = (u32)ldrArgvBuf;
if(R_FAILED(svcCreateEvent(&terminationRequestEvent, RESET_STICKY)))
svcBreak(USERBREAK_ASSERT);
- MyThread *menuThread = menuCreateThread(), *errDispThread = errDispCreateThread(), *hbldrThread = hbldrCreateThread();
+ MyThread *menuThread = menuCreateThread();
MyThread *shellOpenThread = shellOpenCreateThread();
- do
- {
- res = svcWaitSynchronization(notificationHandle, -1LL);
-
- if(R_FAILED(res))
- continue;
-
- u32 notifId = 0;
-
- if(R_FAILED(srvReceiveNotification(¬ifId)))
- svcBreak(USERBREAK_ASSERT);
-
- if(notifId == 0x100)
- {
- // Termination request
- terminationRequest = true;
- svcSignalEvent(terminationRequestEvent);
- }
- }
- while(!terminationRequest);
+ if (R_FAILED(ServiceManager_Run(services, notifications, NULL)))
+ svcBreak(USERBREAK_PANIC);
MyThread_Join(menuThread, -1LL);
- MyThread_Join(errDispThread, -1LL);
- MyThread_Join(hbldrThread, -1LL);
MyThread_Join(shellOpenThread, -1LL);
- svcCloseHandle(notificationHandle);
return 0;
}
diff --git a/sysmodules/rosalina/source/service_manager.c b/sysmodules/rosalina/source/service_manager.c
new file mode 100644
index 0000000..cb9954d
--- /dev/null
+++ b/sysmodules/rosalina/source/service_manager.c
@@ -0,0 +1,166 @@
+/*
+* This file is part of Luma3DS
+* Copyright (C) 2016-2019 Aurora Wright, TuxSH
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program. If not, see .
+*
+* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
+* * Requiring preservation of specified reasonable legal notices or
+* author attributions in that material or in the Appropriate Legal
+* Notices displayed by works containing it.
+* * Prohibiting misrepresentation of the origin of that material,
+* or requiring that modified versions of such material be marked in
+* reasonable ways as different from the original version.
+*/
+
+#include <3ds.h>
+#include "service_manager.h"
+
+#define TRY(expr) if(R_FAILED(res = (expr))) goto cleanup;
+
+Result ServiceManager_Run(const ServiceManagerServiceEntry *services, const ServiceManagerNotificationEntry *notifications, const ServiceManagerContextAllocator *allocator)
+{
+ Result res = 0;
+
+ u32 numServices = 0;
+ u32 maxSessionsTotal = 0;
+ u32 numActiveSessions = 0;
+ bool terminationRequested = false;
+
+ for (u32 i = 0; services[i].name != NULL; i++) {
+ numServices++;
+ maxSessionsTotal += services[i].maxSessions;
+ }
+
+ Handle waitHandles[1 + numServices + maxSessionsTotal];
+ void *ctxs[maxSessionsTotal];
+ u8 handlerIds[maxSessionsTotal];
+
+ Handle replyTarget = 0;
+ s32 id = -1;
+ u32 *cmdbuf = getThreadCommandBuffer();
+
+ TRY(srvEnableNotification(&waitHandles[0]));
+
+ for (u32 i = 0; i < numServices; i++) {
+ if (!services[i].isGlobalPort) {
+ TRY(srvRegisterService(&waitHandles[1 + i], services[i].name, (s32)services[i].maxSessions));
+ } else {
+ Handle clientPort;
+ TRY(svcCreatePort(&waitHandles[1 + i], &clientPort, services[i].name, (s32)services[i].maxSessions));
+ svcCloseHandle(clientPort);
+ }
+ }
+
+ while (!terminationRequested) {
+ if (replyTarget == 0) {
+ cmdbuf[0] = 0xFFFF0000;
+ }
+
+ id = -1;
+ res = svcReplyAndReceive(&id, waitHandles, 1 + numServices + numActiveSessions, replyTarget);
+
+ if (res == (Result)0xC920181A) {
+ // Session has been closed
+ u32 off;
+ if (id == -1) {
+ for (off = 0; off < numActiveSessions && waitHandles[1 + numServices + off] != replyTarget; off++);
+ if (off >= numActiveSessions) {
+ return res;
+ }
+
+ id = 1 + numServices + off;
+ } else if ((u32)id < 1 + numServices) {
+ return res;
+ }
+
+ off = id - 1 - numServices;
+
+ Handle h = waitHandles[id];
+ void *ctx = ctxs[off];
+ waitHandles[id] = waitHandles[1 + numServices + --numActiveSessions];
+ handlerIds[off] = handlerIds[numActiveSessions];
+ ctxs[off] = ctxs[numActiveSessions];
+
+ svcCloseHandle(h);
+ if (allocator != NULL) {
+ allocator->freeSessionContext(ctx);
+ }
+
+ replyTarget = 0;
+ res = 0;
+ } else if (R_FAILED(res)) {
+ return res;
+ }
+
+ else {
+ // Ok, no session closed and no error
+ replyTarget = 0;
+ if (id == 0) {
+ // Notification
+ u32 notificationId = 0;
+ TRY(srvReceiveNotification(¬ificationId));
+ terminationRequested = notificationId == 0x100;
+
+ for (u32 i = 0; notifications[i].handler != NULL; i++) {
+ if (notifications[i].id == notificationId) {
+ notifications[i].handler(notificationId);
+ break;
+ }
+ }
+ } else if ((u32)id < 1 + numServices) {
+ // New session
+ Handle session;
+ void *ctx = NULL;
+ TRY(svcAcceptSession(&session, waitHandles[id]));
+
+ if (allocator) {
+ ctx = allocator->newSessionContext((u8)(id - 1));
+ if (ctx == NULL) {
+ svcCloseHandle(session);
+ return 0xDEAD0000;
+ }
+ }
+
+ waitHandles[1 + numServices + numActiveSessions] = session;
+ handlerIds[numActiveSessions] = (u8)(id - 1);
+ ctxs[numActiveSessions++] = ctx;
+ } else {
+ // Service command
+ u32 off = id - 1 - numServices;
+ services[handlerIds[off]].handler(ctxs[off]);
+ replyTarget = waitHandles[id];
+ }
+ }
+ }
+
+cleanup:
+ for (u32 i = 0; i < 1 + numServices + numActiveSessions; i++) {
+ svcCloseHandle(waitHandles[i]);
+ }
+
+ for (u32 i = 0; i < numServices; i++) {
+ if (!services[i].isGlobalPort) {
+ srvUnregisterService(services[i].name);
+ }
+ }
+
+ if (allocator) {
+ for (u32 i = 0; i < numActiveSessions; i++) {
+ allocator->freeSessionContext(ctxs[i]);
+ }
+ }
+
+ return res;
+}