mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-12 21:46:39 +00:00
54c5677cd0
* codebase: remove all references to Lockpick / Lockpick_RCM. * keys: retrieve Atmosphère's key generation in keysLoadKeyset(). * keys: replace all "key_gen_val" references with "mkey_index". * keys: move latest master key checks in keysReadKeysFromFile() to keysDeriveMasterKeys(). * keys: update latest master key checks to determine if nxdumptool's hardcoded master key vectors are outdated, using the key generation value from Atmosphère. If the master key vectors are outdated, and the newer master key(s) are not available, an error will be displayed. * nxdt_utils: update logic in utilsReplaceIllegalCharacters() to replace consecutive illegal characters with a single underscore. * nxdt_utils: move servicesGetExosphereApiVersion() to nxdt_utils as utilsGetExosphereApiVersion(). * nxdt_utils: define internal UtilsExosphereApiVersion struct, which is used to parse the output from utilsGetExosphereApiVersion(). * nxdt_utils: add utilsGetAtmosphereVersion(), utilsGetAtmosphereKeyGeneration() and utilsGetAtmosphereTargetFirmware() functions. * services: remove servicesGetExosphereApiVersion(). * services: Atmosphère version is now retrieved via utilsGetAtmosphereVersion().
307 lines
9.8 KiB
C
307 lines
9.8 KiB
C
/*
|
|
* services.c
|
|
*
|
|
* Copyright (c) 2020-2023, 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 bool _servicesCheckInitializedServiceByName(const char *name);
|
|
|
|
static Result servicesAtmosphereHasService(bool *out, SmServiceName name);
|
|
|
|
static Result servicesNifmUserInitialize(void);
|
|
static bool servicesClkGetServiceType(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, "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 },
|
|
{ false, "bsd:u", NULL, &socketInitializeDefault, &socketExit }, /* socketInitialize*() functions take care of initializing bsd:* too. */
|
|
{ false, "usb:ds", NULL, &usbDsInitialize, &usbDsExit }
|
|
};
|
|
|
|
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;
|
|
|
|
static u32 g_atmosphereVersion = 0;
|
|
|
|
/* Atmosphère-related constants. */
|
|
static const u32 g_smAtmosphereHasService = 65100;
|
|
static const u32 g_atmosphereTipcVersion = MAKEHOSVERSION(0, 19, 0);
|
|
|
|
bool servicesInitialize(void)
|
|
{
|
|
bool ret = true;
|
|
|
|
SCOPED_LOCK(&g_servicesMutex)
|
|
{
|
|
for(u32 i = 0; i < g_serviceInfoCount; i++)
|
|
{
|
|
ServiceInfo *service_info = &(g_serviceInfo[i]);
|
|
|
|
/* Check if this service has been already initialized. */
|
|
if (service_info->initialized) 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;
|
|
}
|
|
|
|
/* Check if this service actually has a valid initialization function. */
|
|
if (service_info->init_func == NULL) continue;
|
|
|
|
/* Initialize service. */
|
|
Result rc = service_info->init_func();
|
|
if (R_FAILED(rc))
|
|
{
|
|
LOG_MSG_ERROR("Failed to initialize \"%s\" service! (0x%X).", service_info->name, rc);
|
|
ret = false;
|
|
break;
|
|
}
|
|
|
|
/* Update flag. */
|
|
service_info->initialized = true;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void servicesClose(void)
|
|
{
|
|
SCOPED_LOCK(&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 flag. */
|
|
service_info->initialized = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool servicesCheckInitializedServiceByName(const char *name)
|
|
{
|
|
bool ret = false;
|
|
SCOPED_LOCK(&g_servicesMutex) ret = _servicesCheckInitializedServiceByName(name);
|
|
return ret;
|
|
}
|
|
|
|
bool servicesCheckRunningServiceByName(const char *name)
|
|
{
|
|
bool ret = false;
|
|
|
|
SCOPED_LOCK(&g_servicesMutex)
|
|
{
|
|
if (!name || !*name || !_servicesCheckInitializedServiceByName("spl:"))
|
|
{
|
|
LOG_MSG_ERROR("Invalid parameters!");
|
|
break;
|
|
}
|
|
|
|
Result rc = servicesAtmosphereHasService(&ret, smEncodeName(name));
|
|
if (R_FAILED(rc)) LOG_MSG_ERROR("servicesAtmosphereHasService failed for \"%s\"! (0x%X).", name, rc);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate)
|
|
{
|
|
SCOPED_LOCK(&g_servicesMutex)
|
|
{
|
|
if ((g_clkSvcUsePcv && !_servicesCheckInitializedServiceByName("pcv")) || (!g_clkSvcUsePcv && !_servicesCheckInitializedServiceByName("clkrst")))
|
|
{
|
|
LOG_MSG_ERROR("Error: clock service uninitialized.");
|
|
break;
|
|
}
|
|
|
|
Result rc1 = 0, rc2 = 0;
|
|
|
|
if (g_clkSvcUsePcv)
|
|
{
|
|
rc1 = pcvSetClockRate(PcvModule_CpuBus, cpu_rate);
|
|
rc2 = pcvSetClockRate(PcvModule_EMC, mem_rate);
|
|
} else {
|
|
rc1 = clkrstSetClockRate(&g_clkrstCpuSession, cpu_rate);
|
|
rc2 = clkrstSetClockRate(&g_clkrstMemSession, mem_rate);
|
|
}
|
|
|
|
if (R_FAILED(rc1)) LOG_MSG_ERROR("%sSetClockRate failed! (0x%X) (CPU).", (g_clkSvcUsePcv ? "pcv" : "clkrst"), rc1);
|
|
if (R_FAILED(rc2)) LOG_MSG_ERROR("%sSetClockRate failed! (0x%X) (MEM).", (g_clkSvcUsePcv ? "pcv" : "clkrst"), rc2);
|
|
}
|
|
}
|
|
|
|
static bool _servicesCheckInitializedServiceByName(const char *name)
|
|
{
|
|
if (!name || !*name) return false;
|
|
|
|
bool ret = false;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* SM API extension available in Atmosphère and Atmosphère-based CFWs. */
|
|
static Result servicesAtmosphereHasService(bool *out, SmServiceName name)
|
|
{
|
|
if (!out || !name.name[0]) return MAKERESULT(Module_Libnx, LibnxError_BadInput);
|
|
|
|
u8 tmp = 0;
|
|
Result rc = 0;
|
|
|
|
/* Get Atmosphère version if we haven't already. */
|
|
if (!g_atmosphereVersion) g_atmosphereVersion = utilsGetAtmosphereVersion();
|
|
|
|
/* Check if service is running. */
|
|
/* Dispatch IPC request using CMIF or TIPC serialization depending on our current environment. */
|
|
if (hosversionAtLeast(12, 0, 0) || g_atmosphereVersion >= g_atmosphereTipcVersion)
|
|
{
|
|
rc = tipcDispatchInOut(smGetServiceSessionTipc(), g_smAtmosphereHasService, name, tmp);
|
|
} else {
|
|
rc = serviceDispatchInOut(smGetServiceSession(), g_smAtmosphereHasService, name, tmp);
|
|
}
|
|
|
|
if (R_SUCCEEDED(rc)) *out = (tmp != 0);
|
|
|
|
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))
|
|
{
|
|
LOG_MSG_ERROR("clkrstInitialize failed! (0x%X).", 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))
|
|
{
|
|
LOG_MSG_ERROR("clkrstOpenSession failed! (0x%X) (CPU).", rc);
|
|
clkrstExit();
|
|
return rc;
|
|
}
|
|
|
|
rc = clkrstOpenSession(&g_clkrstMemSession, PcvModuleId_EMC, 3);
|
|
if (R_FAILED(rc))
|
|
{
|
|
LOG_MSG_ERROR("clkrstOpenSession failed! (0x%X) (MEM).", 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->cond_func = NULL;
|
|
info->init_func = (g_clkSvcUsePcv ? &pcvInitialize : &servicesClkrstInitialize);
|
|
info->close_func = (g_clkSvcUsePcv ? &pcvExit : &servicesClkrstExit);
|
|
|
|
return true;
|
|
}
|