2020-04-16 01:06:41 +01:00
/*
2020-07-03 10:31:22 +01:00
* services . c
2020-04-16 01:06:41 +01:00
*
2020-12-23 17:48:57 +00:00
* Copyright ( c ) 2020 - 2021 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-07-03 10:31:22 +01:00
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
2021-03-25 19:26:58 +00:00
* 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 .
2020-04-16 01:06:41 +01:00
*
2021-03-25 19:26:58 +00:00
* 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 .
2020-04-16 01:06:41 +01:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 19:26:58 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-04-16 01:06:41 +01:00
*/
2021-03-26 04:35:14 +00:00
# include "nxdt_utils.h"
2020-04-15 21:50:07 +01:00
# include "services.h"
# include "es.h"
/* Type definitions. */
2020-08-15 22:22:49 +01:00
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. */
2020-04-15 21:50:07 +01:00
2020-08-15 22:22:49 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
bool initialized ;
char name [ 8 ] ;
ServiceCondFunction cond_func ;
ServiceInitFunction init_func ;
ServiceCloseFunction close_func ;
2020-08-15 22:22:49 +01:00
} ServiceInfo ;
2020-04-15 21:50:07 +01:00
/* Function prototypes. */
2021-05-18 13:32:43 +01:00
static bool _servicesCheckInitializedServiceByName ( const char * name ) ;
2021-05-11 07:00:33 +01:00
2021-04-20 23:43:37 +01:00
static Result servicesAtmosphereHasService ( bool * out , SmServiceName name ) ;
2021-05-11 07:00:33 +01:00
static Result servicesGetExosphereApiVersion ( u32 * out ) ;
2020-08-15 22:22:49 +01:00
2020-04-15 21:50:07 +01:00
static Result servicesNifmUserInitialize ( void ) ;
static bool servicesClkGetServiceType ( void * arg ) ;
static bool servicesSplCryptoCheckAvailability ( void * arg ) ;
/* Global variables. */
2020-08-15 22:22:49 +01:00
static ServiceInfo g_serviceInfo [ ] = {
2020-04-15 21:50:07 +01:00
{ false , " ncm " , NULL , & ncmInitialize , & ncmExit } ,
{ false , " ns " , NULL , & nsInitialize , & nsExit } ,
{ false , " csrng " , NULL , & csrngInitialize , & csrngExit } ,
2020-08-15 22:22:49 +01:00
{ false , " spl: " , NULL , & splInitialize , & splExit } ,
2020-07-06 01:10:07 +01:00
{ false , " spl:mig " , & servicesSplCryptoCheckAvailability , & splCryptoInitialize , & splCryptoExit } , /* Checks if spl:mig is really available (e.g. avoid calling splInitialize twice). */
2020-04-15 21:50:07 +01:00
{ false , " pm:dmnt " , NULL , & pmdmntInitialize , & pmdmntExit } ,
{ false , " psm " , NULL , & psmInitialize , & psmExit } ,
{ false , " nifm:u " , NULL , & servicesNifmUserInitialize , & nifmExit } ,
2020-07-06 01:10:07 +01:00
{ false , " clk " , & servicesClkGetServiceType , NULL , NULL } , /* Placeholder for pcv / clkrst. */
2020-04-15 21:50:07 +01:00
{ false , " es " , NULL , & esInitialize , & esExit } ,
2021-03-30 20:30:10 +01:00
{ false , " set " , NULL , & setInitialize , & setExit } ,
{ false , " set:sys " , NULL , & setsysInitialize , & setsysExit } ,
2021-07-25 23:23:44 +01:00
{ false , " set:cal " , NULL , & setcalInitialize , & setcalExit } ,
{ false , " bsd:u " , NULL , & socketInitializeDefault , & socketExit } /* socketInitialize*() functions take care of initializing bsd:* too. */
2020-04-15 21:50:07 +01:00
} ;
static const u32 g_serviceInfoCount = MAX_ELEMENTS ( g_serviceInfo ) ;
static bool g_clkSvcUsePcv = false ;
static ClkrstSession g_clkrstCpuSession = { 0 } , g_clkrstMemSession = { 0 } ;
2020-05-03 00:40:50 +01:00
static Mutex g_servicesMutex = 0 ;
2021-05-11 07:00:33 +01:00
static u32 g_atmosphereVersion = 0 ;
2021-04-20 23:43:37 +01:00
/* Atmosphère-related constants. */
static const u32 g_smAtmosphereHasService = 65100 ;
static const SplConfigItem SplConfigItem_ExosphereApiVersion = ( SplConfigItem ) 65000 ;
static const u32 g_atmosphereTipcVersion = MAKEHOSVERSION ( 0 , 19 , 0 ) ;
2020-04-15 21:50:07 +01:00
bool servicesInitialize ( void )
{
bool ret = true ;
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_servicesMutex )
2020-04-15 21:50:07 +01:00
{
2021-05-18 13:32:43 +01:00
for ( u32 i = 0 ; i < g_serviceInfoCount ; i + + )
2020-04-15 21:50:07 +01:00
{
2021-05-18 13:32:43 +01:00
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 ( " Failed to initialize \" %s \" service! (0x%08X). " , service_info - > name , rc ) ;
ret = false ;
break ;
}
/* Update flag. */
service_info - > initialized = true ;
2020-04-15 21:50:07 +01:00
}
}
return ret ;
}
void servicesClose ( void )
{
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_servicesMutex )
2020-04-15 21:50:07 +01:00
{
2021-05-18 13:32:43 +01:00
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 ;
}
2020-04-15 21:50:07 +01:00
}
}
2021-05-11 07:00:33 +01:00
bool servicesCheckInitializedServiceByName ( const char * name )
{
2021-05-18 13:32:43 +01:00
bool ret = false ;
SCOPED_LOCK ( & g_servicesMutex ) ret = _servicesCheckInitializedServiceByName ( name ) ;
return ret ;
2021-05-11 07:00:33 +01:00
}
2020-04-15 21:50:07 +01:00
bool servicesCheckRunningServiceByName ( const char * name )
{
2021-05-18 13:32:43 +01:00
bool ret = false ;
2021-05-11 07:00:33 +01:00
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_servicesMutex )
2021-04-20 23:43:37 +01:00
{
2021-05-18 13:32:43 +01:00
if ( ! name | | ! * name | | ! _servicesCheckInitializedServiceByName ( " spl: " ) )
{
LOG_MSG ( " Invalid parameters! " ) ;
break ;
}
Result rc = servicesAtmosphereHasService ( & ret , smEncodeName ( name ) ) ;
if ( R_FAILED ( rc ) ) LOG_MSG ( " servicesAtmosphereHasService failed for \" %s \" ! (0x%08X). " , name , rc ) ;
2021-04-20 23:43:37 +01:00
}
2020-04-15 21:50:07 +01:00
2021-05-18 13:32:43 +01:00
return ret ;
2020-04-15 21:50:07 +01:00
}
2021-05-11 07:00:33 +01:00
void servicesChangeHardwareClockRates ( u32 cpu_rate , u32 mem_rate )
2020-04-15 21:50:07 +01:00
{
2021-05-18 13:32:43 +01:00
SCOPED_LOCK ( & g_servicesMutex )
2021-05-11 07:00:33 +01:00
{
2021-05-18 13:32:43 +01:00
if ( ( g_clkSvcUsePcv & & ! _servicesCheckInitializedServiceByName ( " pcv " ) ) | | ( ! g_clkSvcUsePcv & & ! _servicesCheckInitializedServiceByName ( " clkrst " ) ) )
{
LOG_MSG ( " 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 ( " %sSetClockRate failed! (0x%08X) (CPU). " , ( g_clkSvcUsePcv ? " pcv " : " clkrst " ) , rc1 ) ;
if ( R_FAILED ( rc2 ) ) LOG_MSG ( " %sSetClockRate failed! (0x%08X) (MEM). " , ( g_clkSvcUsePcv ? " pcv " : " clkrst " ) , rc2 ) ;
2021-05-11 07:00:33 +01:00
}
}
2021-05-18 13:32:43 +01:00
static bool _servicesCheckInitializedServiceByName ( const char * name )
2021-05-11 07:00:33 +01:00
{
2021-05-18 13:32:43 +01:00
if ( ! name | | ! * name ) return false ;
2020-05-03 00:40:50 +01:00
2021-05-18 13:32:43 +01:00
bool ret = false ;
2020-05-03 00:40:50 +01:00
2020-04-15 21:50:07 +01:00
for ( u32 i = 0 ; i < g_serviceInfoCount ; i + + )
{
2020-08-15 22:22:49 +01:00
ServiceInfo * service_info = & ( g_serviceInfo [ i ] ) ;
2021-03-07 23:22:49 +00:00
if ( ! strcmp ( service_info - > name , name ) )
2020-04-15 21:50:07 +01:00
{
2020-08-15 22:22:49 +01:00
ret = service_info - > initialized ;
2020-04-15 21:50:07 +01:00
break ;
}
}
return ret ;
}
2021-04-20 23:43:37 +01:00
/* SM API extension available in Atmosphère and Atmosphère-based CFWs. */
static Result servicesAtmosphereHasService ( bool * out , SmServiceName name )
2020-08-15 22:22:49 +01:00
{
2021-05-11 07:00:33 +01:00
if ( ! out | | ! name . name [ 0 ] ) return MAKERESULT ( Module_Libnx , LibnxError_BadInput ) ;
2021-04-20 23:43:37 +01:00
u8 tmp = 0 ;
Result rc = 0 ;
2021-05-11 07:00:33 +01:00
/* Get Exosphère API version. */
if ( ! g_atmosphereVersion )
{
rc = servicesGetExosphereApiVersion ( & g_atmosphereVersion ) ;
if ( R_FAILED ( rc ) ) LOG_MSG ( " servicesGetExosphereApiVersion failed! (0x%08X). " , rc ) ;
}
2021-04-20 23:43:37 +01:00
/* Check if service is running. */
/* Dispatch IPC request using CMIF or TIPC serialization depending on our current environment. */
2021-05-11 07:00:33 +01:00
if ( hosversionAtLeast ( 12 , 0 , 0 ) | | g_atmosphereVersion > = g_atmosphereTipcVersion )
2020-08-15 22:22:49 +01:00
{
2021-04-20 23:43:37 +01:00
rc = tipcDispatchInOut ( smGetServiceSessionTipc ( ) , g_smAtmosphereHasService , name , tmp ) ;
} else {
rc = serviceDispatchInOut ( smGetServiceSession ( ) , g_smAtmosphereHasService , name , tmp ) ;
2020-08-15 22:22:49 +01:00
}
2021-05-11 07:00:33 +01:00
if ( R_SUCCEEDED ( rc ) ) * out = ( tmp ! = 0 ) ;
2020-08-15 22:22:49 +01:00
return rc ;
}
2021-05-11 07:00:33 +01:00
/* SMC config item available in Atmosphère and Atmosphère-based CFWs. */
static Result servicesGetExosphereApiVersion ( u32 * out )
2020-08-15 22:22:49 +01:00
{
2021-05-11 07:00:33 +01:00
if ( ! out ) return MAKERESULT ( Module_Libnx , LibnxError_BadInput ) ;
2021-04-20 23:43:37 +01:00
Result rc = 0 ;
2021-05-11 07:00:33 +01:00
u64 cfg = 0 ;
u32 version = 0 ;
2021-04-20 23:43:37 +01:00
2021-05-11 07:00:33 +01:00
rc = splGetConfig ( SplConfigItem_ExosphereApiVersion , & cfg ) ;
if ( R_SUCCEEDED ( rc ) )
2021-04-20 23:43:37 +01:00
{
2021-05-11 07:00:33 +01:00
* out = version = ( u32 ) ( ( cfg > > 40 ) & 0xFFFFFF ) ;
LOG_MSG ( " Exosphère API version: %u.%u.%u. " , HOSVER_MAJOR ( version ) , HOSVER_MINOR ( version ) , HOSVER_MICRO ( version ) ) ;
2021-04-20 23:43:37 +01:00
}
2021-05-11 07:00:33 +01:00
return rc ;
2020-08-15 22:22:49 +01:00
}
2020-04-15 21:50:07 +01:00
static Result servicesNifmUserInitialize ( void )
{
return nifmInitialize ( NifmServiceType_User ) ;
}
static Result servicesClkrstInitialize ( void )
{
Result rc = 0 ;
2020-07-06 01:10:07 +01:00
/* Open clkrst service handle. */
2020-04-15 21:50:07 +01:00
rc = clkrstInitialize ( ) ;
2021-05-11 07:00:33 +01:00
if ( R_FAILED ( rc ) )
{
LOG_MSG ( " clkrstInitialize failed! (0x%08X). " , rc ) ;
return rc ;
}
2020-04-15 21:50:07 +01:00
2020-07-06 01:10:07 +01:00
/* Initialize CPU and MEM clkrst sessions. */
2020-04-15 21:50:07 +01:00
memset ( & g_clkrstCpuSession , 0 , sizeof ( ClkrstSession ) ) ;
memset ( & g_clkrstMemSession , 0 , sizeof ( ClkrstSession ) ) ;
rc = clkrstOpenSession ( & g_clkrstCpuSession , PcvModuleId_CpuBus , 3 ) ;
if ( R_FAILED ( rc ) )
{
2021-05-11 07:00:33 +01:00
LOG_MSG ( " clkrstOpenSession failed! (0x%08X) (CPU). " , rc ) ;
2020-04-15 21:50:07 +01:00
clkrstExit ( ) ;
return rc ;
}
rc = clkrstOpenSession ( & g_clkrstMemSession , PcvModuleId_EMC , 3 ) ;
if ( R_FAILED ( rc ) )
{
2021-05-11 07:00:33 +01:00
LOG_MSG ( " clkrstOpenSession failed! (0x%08X) (MEM). " , rc ) ;
2020-04-15 21:50:07 +01:00
clkrstCloseSession ( & g_clkrstCpuSession ) ;
clkrstExit ( ) ;
}
return rc ;
}
static void servicesClkrstExit ( void )
{
2020-07-06 01:10:07 +01:00
/* Close CPU and MEM clkrst sessions. */
2020-04-15 21:50:07 +01:00
clkrstCloseSession ( & g_clkrstMemSession ) ;
clkrstCloseSession ( & g_clkrstCpuSession ) ;
2020-07-06 01:10:07 +01:00
/* Close clkrst service handle. */
2020-04-15 21:50:07 +01:00
clkrstExit ( ) ;
}
static bool servicesClkGetServiceType ( void * arg )
{
if ( ! arg ) return false ;
2020-08-15 22:22:49 +01:00
ServiceInfo * info = ( ServiceInfo * ) arg ;
2021-03-07 23:22:49 +00:00
if ( strcmp ( info - > name , " clk " ) ! = 0 | | info - > init_func ! = NULL | | info - > close_func ! = NULL ) return false ;
2020-04-15 21:50:07 +01:00
2020-07-06 01:10:07 +01:00
/* 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). */
2020-04-15 21:50:07 +01:00
g_clkSvcUsePcv = hosversionBefore ( 8 , 0 , 0 ) ;
2020-07-06 01:10:07 +01:00
/* Fill service info. */
2020-04-15 21:50:07 +01:00
sprintf ( info - > name , " %s " , ( g_clkSvcUsePcv ? " pcv " : " clkrst " ) ) ;
2021-05-11 07:00:33 +01:00
info - > cond_func = NULL ;
2020-04-15 21:50:07 +01:00
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 ;
2020-08-15 22:22:49 +01:00
ServiceInfo * info = ( ServiceInfo * ) arg ;
2021-03-07 23:22:49 +00:00
if ( strcmp ( info - > name , " spl:mig " ) ! = 0 | | info - > init_func = = NULL | | info - > close_func = = NULL ) return false ;
2020-04-15 21:50:07 +01:00
2020-07-06 01:10:07 +01:00
/* Check if spl:mig is available (sysver equal to or greater than 4.0.0). */
2021-05-11 07:00:33 +01:00
return hosversionAtLeast ( 4 , 0 , 0 ) ;
2020-04-15 21:50:07 +01:00
}