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; +}