
- Each service must have xyzInit/xyzExit (with that name) - xyzInit/xyzExit use reference counting - xyzExit returns void - The utilities in <3ds/result.h> are used instead of manual error checking - The intrinsics in <3ds/synchronization.h> are used instead of inline asm - Other miscellaneous changes - APT now uses a lightweight lock instead of a mutex - Initial handle parameters in PTMU were killed - Explicit init'ion to 0 or NULL has been removed for global variables since they end up on .bss anyway - MIC hasn't been touched because it must be rewritten first - CFGNOR needs a slight touch before converting - SOC is still to be cleaned up
224 lines
4.5 KiB
C
224 lines
4.5 KiB
C
/*
|
|
srv.c _ Service manager.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <3ds/types.h>
|
|
#include <3ds/result.h>
|
|
#include <3ds/srv.h>
|
|
#include <3ds/svc.h>
|
|
#include <3ds/ipc.h>
|
|
|
|
|
|
/*
|
|
The homebrew loader can choose to supply a list of service handles that have
|
|
been "stolen" from other processes that have been compromised. This allows us
|
|
to access services that are normally restricted from the current process.
|
|
|
|
For every service requested by the application, we shall first check if the
|
|
list given to us contains the requested service and if so use it. If we don't
|
|
find the service in that list, we ask the service manager and hope for the
|
|
best.
|
|
*/
|
|
|
|
typedef struct {
|
|
u32 num;
|
|
|
|
struct {
|
|
char name[8];
|
|
Handle handle;
|
|
} services[];
|
|
} service_list_t;
|
|
|
|
extern service_list_t* __service_ptr;
|
|
|
|
static Handle g_srv_handle;
|
|
|
|
|
|
static int __name_cmp(const char* a, const char* b) {
|
|
u32 i;
|
|
|
|
for(i=0; i<8; i++) {
|
|
if(a[i] != b[i])
|
|
return 1;
|
|
if(a[i] == '\0')
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Handle __get_handle_from_list(const char* name) {
|
|
if((u32)__service_ptr == 0)
|
|
return 0;
|
|
|
|
u32 i, num = __service_ptr->num;
|
|
|
|
for(i=0; i<num; i++) {
|
|
if(__name_cmp(__service_ptr->services[i].name, name) == 0)
|
|
return __service_ptr->services[i].handle;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void __destroy_handle_list(void) {
|
|
if((u32)__service_ptr == 0)
|
|
return;
|
|
|
|
u32 i, num = __service_ptr->num;
|
|
|
|
for(i=0; i<num; i++)
|
|
svcCloseHandle(__service_ptr->services[i].handle);
|
|
|
|
__service_ptr->num = 0;
|
|
}
|
|
|
|
|
|
Result srvInit(void)
|
|
{
|
|
Result rc = 0;
|
|
|
|
if(g_srv_handle != 0) return rc;
|
|
|
|
if(R_FAILED(rc = svcConnectToPort(&g_srv_handle, "srv:")))return rc;
|
|
|
|
if(R_FAILED(rc = srvRegisterClient())) {
|
|
svcCloseHandle(g_srv_handle);
|
|
g_srv_handle = 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvExit(void)
|
|
{
|
|
if(g_srv_handle != 0)svcCloseHandle(g_srv_handle);
|
|
|
|
g_srv_handle = 0;
|
|
return 0;
|
|
}
|
|
|
|
Handle *srvGetSessionHandle(void)
|
|
{
|
|
return &g_srv_handle;
|
|
}
|
|
|
|
Result srvRegisterClient(void)
|
|
{
|
|
Result rc = 0;
|
|
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x1,0,2); // 0x10002
|
|
cmdbuf[1] = IPC_Desc_CurProcessHandle();
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(g_srv_handle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvGetServiceHandleDirect(Handle* out, const char* name)
|
|
{
|
|
Result rc = 0;
|
|
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
cmdbuf[0] = IPC_MakeHeader(0x5,4,0); // 0x50100
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strlen(name);
|
|
cmdbuf[4] = 0x0;
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(g_srv_handle)))return rc;
|
|
|
|
*out = cmdbuf[3];
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvGetServiceHandle(Handle* out, const char* name)
|
|
{
|
|
/* Look in service-list given to us by loader. If we find find a match,
|
|
we return it. */
|
|
Handle h = __get_handle_from_list(name);
|
|
|
|
if(h != 0) {
|
|
return svcDuplicateHandle(out, h);
|
|
}
|
|
|
|
/* Normal request to service manager. */
|
|
return srvGetServiceHandleDirect(out, name);
|
|
}
|
|
|
|
Result srvRegisterService(Handle* out, const char* name, int maxSessions)
|
|
{
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
cmdbuf[0] = IPC_MakeHeader(0x3,4,0); // 0x30100
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strlen(name);
|
|
cmdbuf[4] = maxSessions;
|
|
|
|
Result rc;
|
|
if(R_FAILED(rc = svcSendSyncRequest(g_srv_handle)))return rc;
|
|
|
|
*out = cmdbuf[3];
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvUnregisterService(const char* name)
|
|
{
|
|
u32* cmdbuf = getThreadCommandBuffer();
|
|
cmdbuf[0] = IPC_MakeHeader(0x4,3,0); // 0x400C0
|
|
strncpy((char*) &cmdbuf[1], name,8);
|
|
cmdbuf[3] = strlen(name);
|
|
|
|
Result rc;
|
|
if(R_FAILED(rc = svcSendSyncRequest(g_srv_handle)))return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
// Old srv:pm interface, will only work on systems where srv:pm was a port (<7.X)
|
|
Result srvPmInit(void)
|
|
{
|
|
Result rc = 0;
|
|
|
|
if(R_FAILED(rc = svcConnectToPort(&g_srv_handle, "srv:pm")))return rc;
|
|
|
|
if(R_FAILED(rc = srvRegisterClient())) {
|
|
svcCloseHandle(g_srv_handle);
|
|
g_srv_handle = 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
Result srvRegisterProcess(u32 procid, u32 count, void *serviceaccesscontrol)
|
|
{
|
|
Result rc = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x403,2,2); // 0x4030082 // <7.x
|
|
cmdbuf[1] = procid;
|
|
cmdbuf[2] = count;
|
|
cmdbuf[3] = IPC_Desc_StaticBuffer(count*4,0);
|
|
cmdbuf[4] = (u32)serviceaccesscontrol;
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(g_srv_handle))) return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|
|
|
|
Result srvUnregisterProcess(u32 procid)
|
|
{
|
|
Result rc = 0;
|
|
|
|
u32 *cmdbuf = getThreadCommandBuffer();
|
|
|
|
cmdbuf[0] = IPC_MakeHeader(0x404,1,0); // 0x4040040 // <7.x
|
|
cmdbuf[1] = procid;
|
|
|
|
if(R_FAILED(rc = svcSendSyncRequest(g_srv_handle))) return rc;
|
|
|
|
return cmdbuf[1];
|
|
}
|