mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-10 12:41:47 +00:00
7c2d16714c
These files will be modified to slowly add features from the nxdumptool rewrite codebase. This commit effectively makes it impossible to build the previous PoC. The code from each PoC will be reused, though, so it hasn't been removed.
293 lines
9 KiB
C
293 lines
9 KiB
C
/*
|
|
* services.c
|
|
*
|
|
* Copyright (c) 2020-2021, DarkMatterCore <pabloacurielz@gmail.com>.
|
|
*
|
|
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
|
|
*
|
|
* nxdumptool 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.
|
|
*
|
|
* nxdumptool 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "nxdt_utils.h"
|
|
#include "services.h"
|
|
#include "es.h"
|
|
|
|
/* Type definitions. */
|
|
|
|
typedef bool (*ServiceCondFunction)(void *arg); /* Used to perform a runtime condition check (e.g. system version) before initializing the service. */
|
|
typedef Result (*ServiceInitFunction)(void); /* Used to initialize the service. */
|
|
typedef void (*ServiceCloseFunction)(void); /* Used to close the service. */
|
|
|
|
typedef struct {
|
|
bool initialized;
|
|
char name[8];
|
|
ServiceCondFunction cond_func;
|
|
ServiceInitFunction init_func;
|
|
ServiceCloseFunction close_func;
|
|
} ServiceInfo;
|
|
|
|
/* Function prototypes. */
|
|
|
|
static Result smAtmosphereHasService(bool *out, SmServiceName name);
|
|
|
|
static Result servicesNifmUserInitialize(void);
|
|
static bool servicesClkGetServiceType(void *arg);
|
|
static bool servicesSplCryptoCheckAvailability(void *arg);
|
|
|
|
/* Global variables. */
|
|
|
|
static ServiceInfo g_serviceInfo[] = {
|
|
{ false, "ncm", NULL, &ncmInitialize, &ncmExit },
|
|
{ false, "ns", NULL, &nsInitialize, &nsExit },
|
|
{ false, "csrng", NULL, &csrngInitialize, &csrngExit },
|
|
{ false, "spl:", NULL, &splInitialize, &splExit },
|
|
{ false, "spl:mig", &servicesSplCryptoCheckAvailability, &splCryptoInitialize, &splCryptoExit }, /* Checks if spl:mig is really available (e.g. avoid calling splInitialize twice). */
|
|
{ false, "pm:dmnt", NULL, &pmdmntInitialize, &pmdmntExit },
|
|
{ false, "psm", NULL, &psmInitialize, &psmExit },
|
|
{ false, "nifm:u", NULL, &servicesNifmUserInitialize, &nifmExit },
|
|
{ false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst. */
|
|
{ false, "es", NULL, &esInitialize, &esExit },
|
|
{ false, "set", NULL, &setInitialize, &setExit },
|
|
{ false, "set:sys", NULL, &setsysInitialize, &setsysExit },
|
|
{ false, "set:cal", NULL, &setcalInitialize, &setcalExit }
|
|
};
|
|
|
|
static const u32 g_serviceInfoCount = MAX_ELEMENTS(g_serviceInfo);
|
|
|
|
static bool g_clkSvcUsePcv = false;
|
|
static ClkrstSession g_clkrstCpuSession = {0}, g_clkrstMemSession = {0};
|
|
|
|
static Mutex g_servicesMutex = 0;
|
|
|
|
bool servicesInitialize(void)
|
|
{
|
|
mutexLock(&g_servicesMutex);
|
|
|
|
Result rc = 0;
|
|
bool ret = true;
|
|
|
|
for(u32 i = 0; i < g_serviceInfoCount; i++)
|
|
{
|
|
ServiceInfo *service_info = &(g_serviceInfo[i]);
|
|
|
|
/* Check if this service has been already initialized or if it actually has a valid initialize function. */
|
|
if (service_info->initialized || service_info->init_func == NULL) continue;
|
|
|
|
/* Check if this service depends on a condition function. */
|
|
if (service_info->cond_func != NULL)
|
|
{
|
|
/* Run the condition function - it will update the current service member. */
|
|
/* Skip this service if the required conditions aren't met. */
|
|
if (!service_info->cond_func(service_info)) continue;
|
|
}
|
|
|
|
/* Initialize service. */
|
|
rc = service_info->init_func();
|
|
if (R_FAILED(rc))
|
|
{
|
|
LOG_MSG("Failed to initialize %s service! (0x%08X).", service_info->name, rc);
|
|
ret = false;
|
|
break;
|
|
}
|
|
|
|
/* Update initialized flag. */
|
|
service_info->initialized = true;
|
|
}
|
|
|
|
mutexUnlock(&g_servicesMutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void servicesClose(void)
|
|
{
|
|
mutexLock(&g_servicesMutex);
|
|
|
|
for(u32 i = 0; i < g_serviceInfoCount; i++)
|
|
{
|
|
ServiceInfo *service_info = &(g_serviceInfo[i]);
|
|
|
|
/* Check if this service has not been initialized, or if it doesn't have a valid close function. */
|
|
if (!service_info->initialized || service_info->close_func == NULL) continue;
|
|
|
|
/* Close service. */
|
|
service_info->close_func();
|
|
|
|
/* Update initialized flag. */
|
|
service_info->initialized = false;
|
|
}
|
|
|
|
mutexUnlock(&g_servicesMutex);
|
|
}
|
|
|
|
bool servicesCheckRunningServiceByName(const char *name)
|
|
{
|
|
if (!name || !*name) return false;
|
|
|
|
Result rc = 0;
|
|
Handle handle = INVALID_HANDLE;
|
|
SmServiceName service_name = smEncodeName(name);
|
|
bool running = false;
|
|
|
|
rc = smRegisterService(&handle, service_name, false, 1);
|
|
if (R_FAILED(rc)) LOG_MSG("smRegisterService failed for \"%s\"! (0x%08X).", name, rc);
|
|
running = R_FAILED(rc);
|
|
|
|
if (handle != INVALID_HANDLE) svcCloseHandle(handle);
|
|
if (!running) smUnregisterService(service_name);
|
|
|
|
return running;
|
|
}
|
|
|
|
bool servicesCheckInitializedServiceByName(const char *name)
|
|
{
|
|
mutexLock(&g_servicesMutex);
|
|
|
|
bool ret = false;
|
|
|
|
if (!name || !*name) goto end;
|
|
|
|
for(u32 i = 0; i < g_serviceInfoCount; i++)
|
|
{
|
|
ServiceInfo *service_info = &(g_serviceInfo[i]);
|
|
|
|
if (!strcmp(service_info->name, name))
|
|
{
|
|
ret = service_info->initialized;
|
|
break;
|
|
}
|
|
}
|
|
|
|
end:
|
|
mutexUnlock(&g_servicesMutex);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate)
|
|
{
|
|
mutexLock(&g_servicesMutex);
|
|
|
|
if (g_clkSvcUsePcv)
|
|
{
|
|
pcvSetClockRate(PcvModule_CpuBus, cpu_rate);
|
|
pcvSetClockRate(PcvModule_EMC, mem_rate);
|
|
} else {
|
|
clkrstSetClockRate(&g_clkrstCpuSession, cpu_rate);
|
|
clkrstSetClockRate(&g_clkrstMemSession, mem_rate);
|
|
}
|
|
|
|
mutexUnlock(&g_servicesMutex);
|
|
}
|
|
|
|
Result servicesHasService(bool *out, const char *name)
|
|
{
|
|
if (!out || !name || !*name)
|
|
{
|
|
LOG_MSG("Invalid parameters!");
|
|
return MAKERESULT(Module_Libnx, LibnxError_IoError);
|
|
}
|
|
|
|
*out = false;
|
|
Result rc = 0;
|
|
SmServiceName service_name = smEncodeName(name);
|
|
|
|
rc = smAtmosphereHasService(out, service_name);
|
|
if (R_FAILED(rc)) LOG_MSG("smAtmosphereHasService failed for \"%s\"! (0x%08X).", name, rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* SM API extension available in Atmosphère and Atmosphère-based CFWs. */
|
|
static Result smAtmosphereHasService(bool *out, SmServiceName name)
|
|
{
|
|
u8 tmp = 0;
|
|
Result rc = serviceDispatchInOut(smGetServiceSession(), 65100, name, tmp);
|
|
if (R_SUCCEEDED(rc) && out) *out = tmp;
|
|
return rc;
|
|
}
|
|
|
|
static Result servicesNifmUserInitialize(void)
|
|
{
|
|
return nifmInitialize(NifmServiceType_User);
|
|
}
|
|
|
|
static Result servicesClkrstInitialize(void)
|
|
{
|
|
Result rc = 0;
|
|
|
|
/* Open clkrst service handle. */
|
|
rc = clkrstInitialize();
|
|
if (R_FAILED(rc)) return rc;
|
|
|
|
/* Initialize CPU and MEM clkrst sessions. */
|
|
memset(&g_clkrstCpuSession, 0, sizeof(ClkrstSession));
|
|
memset(&g_clkrstMemSession, 0, sizeof(ClkrstSession));
|
|
|
|
rc = clkrstOpenSession(&g_clkrstCpuSession, PcvModuleId_CpuBus, 3);
|
|
if (R_FAILED(rc))
|
|
{
|
|
clkrstExit();
|
|
return rc;
|
|
}
|
|
|
|
rc = clkrstOpenSession(&g_clkrstMemSession, PcvModuleId_EMC, 3);
|
|
if (R_FAILED(rc))
|
|
{
|
|
clkrstCloseSession(&g_clkrstCpuSession);
|
|
clkrstExit();
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void servicesClkrstExit(void)
|
|
{
|
|
/* Close CPU and MEM clkrst sessions. */
|
|
clkrstCloseSession(&g_clkrstMemSession);
|
|
clkrstCloseSession(&g_clkrstCpuSession);
|
|
|
|
/* Close clkrst service handle. */
|
|
clkrstExit();
|
|
}
|
|
|
|
static bool servicesClkGetServiceType(void *arg)
|
|
{
|
|
if (!arg) return false;
|
|
|
|
ServiceInfo *info = (ServiceInfo*)arg;
|
|
if (strcmp(info->name, "clk") != 0 || info->init_func != NULL || info->close_func != NULL) return false;
|
|
|
|
/* Determine which service needs to be used to control hardware clock rates, depending on the system version. */
|
|
/* This may either be pcv (sysver lower than 8.0.0) or clkrst (sysver equal to or greater than 8.0.0). */
|
|
g_clkSvcUsePcv = hosversionBefore(8, 0, 0);
|
|
|
|
/* Fill service info. */
|
|
sprintf(info->name, "%s", (g_clkSvcUsePcv ? "pcv" : "clkrst"));
|
|
info->init_func = (g_clkSvcUsePcv ? &pcvInitialize : &servicesClkrstInitialize);
|
|
info->close_func = (g_clkSvcUsePcv ? &pcvExit : &servicesClkrstExit);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool servicesSplCryptoCheckAvailability(void *arg)
|
|
{
|
|
if (!arg) return false;
|
|
|
|
ServiceInfo *info = (ServiceInfo*)arg;
|
|
if (strcmp(info->name, "spl:mig") != 0 || info->init_func == NULL || info->close_func == NULL) return false;
|
|
|
|
/* Check if spl:mig is available (sysver equal to or greater than 4.0.0). */
|
|
return !hosversionBefore(4, 0, 0);
|
|
}
|