Introduce service_manager, also fix hb:ldr pm/pm race condition
This commit is contained in:
parent
f72d99d9b0
commit
474eb3001b
@ -27,8 +27,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
#include "MyThread.h"
|
||||
|
||||
MyThread *errDispCreateThread(void);
|
||||
|
||||
void errDispThreadMain(void);
|
||||
void ERRF_HandleCommands(void *ctx);
|
||||
|
@ -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);
|
||||
|
48
sysmodules/rosalina/include/service_manager.h
Normal file
48
sysmodules/rosalina/include/service_manager.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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);
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Result res = 0;
|
||||
Handle notificationHandle;
|
||||
|
||||
if(R_FAILED(srvEnableNotification(¬ificationHandle)))
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
|
||||
if(R_FAILED(svcCreateEvent(&terminationRequestEvent, RESET_STICKY)))
|
||||
svcBreak(USERBREAK_ASSERT);
|
||||
|
||||
MyThread *menuThread = menuCreateThread(), *errDispThread = errDispCreateThread(), *hbldrThread = hbldrCreateThread();
|
||||
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)
|
||||
static void handleTermNotification(u32 notificationId)
|
||||
{
|
||||
(void)notificationId;
|
||||
// Termination request
|
||||
terminationRequest = true;
|
||||
svcSignalEvent(terminationRequestEvent);
|
||||
}
|
||||
}
|
||||
while(!terminationRequest);
|
||||
|
||||
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)
|
||||
{
|
||||
static u8 ipcBuf[0x100] = {0}; // used by both err:f and hb:ldr
|
||||
|
||||
// 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();
|
||||
MyThread *shellOpenThread = shellOpenCreateThread();
|
||||
|
||||
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;
|
||||
}
|
||||
|
166
sysmodules/rosalina/source/service_manager.c
Normal file
166
sysmodules/rosalina/source/service_manager.c
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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;
|
||||
}
|
Reference in New Issue
Block a user