Introduce service_manager, also fix hb:ldr pm/pm race condition

This commit is contained in:
TuxSH 2019-03-16 23:40:43 +01:00
parent f72d99d9b0
commit 474eb3001b
7 changed files with 252 additions and 186 deletions

View File

@ -27,8 +27,5 @@
#pragma once
#include <3ds/types.h>
#include "MyThread.h"
MyThread *errDispCreateThread(void);
void errDispThreadMain(void);
void ERRF_HandleCommands(void *ctx);

View File

@ -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);

View 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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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(&notificationHandle)))
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(&notifId)))
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;
}

View 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(&notificationId));
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;
}