mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-10 07:06:34 +00:00
sm: reimplement using tipc instead of cmif (probably broken, untested)
This commit is contained in:
parent
58776f5ba8
commit
57c8bc432d
27 changed files with 904 additions and 735 deletions
|
@ -21,7 +21,7 @@ PRECOMPILED_HEADERS := $(CURRENT_DIRECTORY)/include/stratosphere.hpp
|
||||||
#PRECOMPILED_HEADERS :=
|
#PRECOMPILED_HEADERS :=
|
||||||
|
|
||||||
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
|
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
|
||||||
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
|
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -flto
|
||||||
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
|
||||||
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
|
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
|
||||||
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
|
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)
|
||||||
|
|
|
@ -25,4 +25,3 @@
|
||||||
|
|
||||||
#include <stratosphere/sm/impl/sm_user_interface.hpp>
|
#include <stratosphere/sm/impl/sm_user_interface.hpp>
|
||||||
#include <stratosphere/sm/impl/sm_manager_interface.hpp>
|
#include <stratosphere/sm/impl/sm_manager_interface.hpp>
|
||||||
#include <stratosphere/sm/impl/sm_debug_monitor_interface.hpp>
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <vapours.hpp>
|
|
||||||
#include <stratosphere/sm/sm_types.hpp>
|
|
||||||
#include <stratosphere/sf.hpp>
|
|
||||||
|
|
||||||
#define AMS_SM_I_DEBUG_MONITOR_INTERFACE_INTERFACE_INFO(C, H) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereGetRecord, (sf::Out<sm::ServiceRecord> record, sm::ServiceName service), (record, service)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 65001, void, AtmosphereListRecords, (const sf::OutArray<sm::ServiceRecord> &records, sf::Out<u64> out_count, u64 offset), (records, out_count, offset)) \
|
|
||||||
AMS_SF_METHOD_INFO(C, H, 65002, void, AtmosphereGetRecordSize, (sf::Out<u64> record_size), (record_size))
|
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::sm::impl, IDebugMonitorInterface, AMS_SM_I_DEBUG_MONITOR_INTERFACE_INTERFACE_INFO)
|
|
|
@ -17,13 +17,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#include <stratosphere/sm/sm_types.hpp>
|
#include <stratosphere/sm/sm_types.hpp>
|
||||||
#include <stratosphere/sf.hpp>
|
#include <stratosphere/tipc.hpp>
|
||||||
|
|
||||||
#define AMS_SM_I_MANAGER_INTERFACE_INTERFACE_INFO(C, H) \
|
#define AMS_SM_I_MANAGER_INTERFACE_INTERFACE_INFO(C, H) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterProcess, (os::ProcessId process_id, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac), (process_id, acid_sac, aci_sac)) \
|
AMS_TIPC_METHOD_INFO(C, H, 0, Result, RegisterProcess, (os::ProcessId process_id, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac), (process_id, acid_sac, aci_sac)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 1, Result, UnregisterProcess, (os::ProcessId process_id), (process_id)) \
|
AMS_TIPC_METHOD_INFO(C, H, 1, Result, UnregisterProcess, (os::ProcessId process_id), (process_id)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65000, void, AtmosphereEndInitDefers, (), ()) \
|
AMS_TIPC_METHOD_INFO(C, H, 65000, void, AtmosphereEndInitDefers, (), ()) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65001, void, AtmosphereHasMitm, (sf::Out<bool> out, sm::ServiceName service), (out, service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65001, void, AtmosphereHasMitm, (tipc::Out<bool> out, sm::ServiceName service), (out, service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65002, Result, AtmosphereRegisterProcess, (os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac), (process_id, program_id, override_status, acid_sac, aci_sac))
|
AMS_TIPC_METHOD_INFO(C, H, 65002, Result, AtmosphereRegisterProcess, (os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac), (process_id, program_id, override_status, acid_sac, aci_sac))
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::sm::impl, IManagerInterface, AMS_SM_I_MANAGER_INTERFACE_INTERFACE_INFO)
|
AMS_TIPC_DEFINE_INTERFACE(ams::sm::impl, IManagerInterface, AMS_SM_I_MANAGER_INTERFACE_INTERFACE_INFO)
|
||||||
|
|
|
@ -17,22 +17,22 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#include <stratosphere/sm/sm_types.hpp>
|
#include <stratosphere/sm/sm_types.hpp>
|
||||||
#include <stratosphere/sf.hpp>
|
#include <stratosphere/tipc.hpp>
|
||||||
|
|
||||||
#define AMS_SM_I_USER_INTERFACE_INTERFACE_INFO(C, H) \
|
#define AMS_SM_I_USER_INTERFACE_INTERFACE_INFO(C, H) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterClient, (const sf::ClientProcessId &client_process_id), (client_process_id)) \
|
AMS_TIPC_METHOD_INFO(C, H, 0, Result, RegisterClient, (const tipc::ClientProcessId client_process_id), (client_process_id)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetServiceHandle, (sf::OutMoveHandle out_h, sm::ServiceName service), (out_h, service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 1, Result, GetServiceHandle, (tipc::OutMoveHandle out_h, sm::ServiceName service), (out_h, service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 2, Result, RegisterService, (sf::OutMoveHandle out_h, sm::ServiceName service, u32 max_sessions, bool is_light), (out_h, service, max_sessions, is_light)) \
|
AMS_TIPC_METHOD_INFO(C, H, 2, Result, RegisterService, (tipc::OutMoveHandle out_h, sm::ServiceName service, u32 max_sessions, bool is_light), (out_h, service, max_sessions, is_light)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 3, Result, UnregisterService, (sm::ServiceName service), (service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 3, Result, UnregisterService, (sm::ServiceName service), (service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 4, Result, DetachClient, (const sf::ClientProcessId &client_process_id), (client_process_id)) \
|
AMS_TIPC_METHOD_INFO(C, H, 4, Result, DetachClient, (const tipc::ClientProcessId client_process_id), (client_process_id)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereInstallMitm, (sf::OutMoveHandle srv_h, sf::OutMoveHandle qry_h, sm::ServiceName service), (srv_h, qry_h, service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65000, Result, AtmosphereInstallMitm, (tipc::OutMoveHandle srv_h, tipc::OutMoveHandle qry_h, sm::ServiceName service), (srv_h, qry_h, service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65001, Result, AtmosphereUninstallMitm, (sm::ServiceName service), (service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65001, Result, AtmosphereUninstallMitm, (sm::ServiceName service), (service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65003, Result, AtmosphereAcknowledgeMitmSession, (sf::Out<sm::MitmProcessInfo> client_info, sf::OutMoveHandle fwd_h, sm::ServiceName service), (client_info, fwd_h, service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65003, Result, AtmosphereAcknowledgeMitmSession, (tipc::Out<sm::MitmProcessInfo> client_info, tipc::OutMoveHandle fwd_h, sm::ServiceName service), (client_info, fwd_h, service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65004, Result, AtmosphereHasMitm, (sf::Out<bool> out, sm::ServiceName service), (out, service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65004, Result, AtmosphereHasMitm, (tipc::Out<bool> out, sm::ServiceName service), (out, service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65005, Result, AtmosphereWaitMitm, (sm::ServiceName service), (service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65005, Result, AtmosphereWaitMitm, (sm::ServiceName service), (service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65006, Result, AtmosphereDeclareFutureMitm, (sm::ServiceName service), (service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65006, Result, AtmosphereDeclareFutureMitm, (sm::ServiceName service), (service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65007, Result, AtmosphereClearFutureMitm, (sm::ServiceName service), (service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65007, Result, AtmosphereClearFutureMitm, (sm::ServiceName service), (service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65100, Result, AtmosphereHasService, (sf::Out<bool> out, sm::ServiceName service), (out, service)) \
|
AMS_TIPC_METHOD_INFO(C, H, 65100, Result, AtmosphereHasService, (tipc::Out<bool> out, sm::ServiceName service), (out, service)) \
|
||||||
AMS_SF_METHOD_INFO(C, H, 65101, Result, AtmosphereWaitService, (sm::ServiceName service), (service))
|
AMS_TIPC_METHOD_INFO(C, H, 65101, Result, AtmosphereWaitService, (sm::ServiceName service), (service))
|
||||||
|
|
||||||
AMS_SF_DEFINE_INTERFACE(ams::sm::impl, IUserInterface, AMS_SM_I_USER_INTERFACE_INTERFACE_INFO)
|
AMS_TIPC_DEFINE_INTERFACE(ams::sm::impl, IUserInterface, AMS_SM_I_USER_INTERFACE_INTERFACE_INFO)
|
||||||
|
|
|
@ -55,18 +55,6 @@ namespace ams::sm {
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For Debug Monitor extensions. */
|
|
||||||
struct ServiceRecord {
|
|
||||||
ServiceName service;
|
|
||||||
os::ProcessId owner_process_id;
|
|
||||||
u64 max_sessions;
|
|
||||||
os::ProcessId mitm_process_id;
|
|
||||||
os::ProcessId mitm_waiting_ack_process_id;
|
|
||||||
bool is_light;
|
|
||||||
bool mitm_waiting_ack;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(ServiceRecord) == 0x30, "ServiceRecord definition!");
|
|
||||||
|
|
||||||
/* For Mitm extensions. */
|
/* For Mitm extensions. */
|
||||||
struct MitmProcessInfo {
|
struct MitmProcessInfo {
|
||||||
os::ProcessId process_id;
|
os::ProcessId process_id;
|
||||||
|
|
|
@ -280,7 +280,7 @@ namespace ams::tipc::impl {
|
||||||
static constexpr size_t OutDataSize = util::AlignUp(OutDataOffsets[NumOutDatas], alignof(u32));
|
static constexpr size_t OutDataSize = util::AlignUp(OutDataOffsets[NumOutDatas], alignof(u32));
|
||||||
|
|
||||||
/* Useful because reasons. */
|
/* Useful because reasons. */
|
||||||
static constexpr size_t OutDataAlign = [] {
|
static constexpr size_t OutDataAlign = []() -> size_t {
|
||||||
if constexpr (std::tuple_size<OutDatas>::value) {
|
if constexpr (std::tuple_size<OutDatas>::value) {
|
||||||
return alignof(typename std::tuple_element<0, OutDatas>::type);
|
return alignof(typename std::tuple_element<0, OutDatas>::type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,8 @@ namespace ams::tipc {
|
||||||
public:
|
public:
|
||||||
constexpr ALWAYS_INLINE InArrayImpl() : BaseType() { /* ... */ }
|
constexpr ALWAYS_INLINE InArrayImpl() : BaseType() { /* ... */ }
|
||||||
constexpr ALWAYS_INLINE InArrayImpl(const tipc::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
constexpr ALWAYS_INLINE InArrayImpl(const tipc::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
||||||
|
constexpr ALWAYS_INLINE InArrayImpl(uintptr_t ptr, size_t sz) : BaseType(ptr, sz) { /* ... */ }
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE InArrayImpl(const T *ptr, size_t num_elements) : BaseType(reinterpret_cast<uintptr_t>(ptr), num_elements * sizeof(T)) { /* ... */ }
|
constexpr ALWAYS_INLINE InArrayImpl(const T *ptr, size_t num_elements) : BaseType(reinterpret_cast<uintptr_t>(ptr), num_elements * sizeof(T)) { /* ... */ }
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE const T *GetPointer() const {
|
constexpr ALWAYS_INLINE const T *GetPointer() const {
|
||||||
|
@ -163,6 +165,8 @@ namespace ams::tipc {
|
||||||
public:
|
public:
|
||||||
constexpr ALWAYS_INLINE OutArrayImpl() : BaseType() { /* ... */ }
|
constexpr ALWAYS_INLINE OutArrayImpl() : BaseType() { /* ... */ }
|
||||||
constexpr ALWAYS_INLINE OutArrayImpl(const tipc::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
constexpr ALWAYS_INLINE OutArrayImpl(const tipc::PointerAndSize &_pas) : BaseType(_pas) { /* ... */ }
|
||||||
|
constexpr ALWAYS_INLINE OutArrayImpl(uintptr_t ptr, size_t sz) : BaseType(ptr, sz) { /* ... */ }
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE OutArrayImpl(T *ptr, size_t num_elements) : BaseType(reinterpret_cast<uintptr_t>(ptr), num_elements * sizeof(T)) { /* ... */ }
|
constexpr ALWAYS_INLINE OutArrayImpl(T *ptr, size_t num_elements) : BaseType(reinterpret_cast<uintptr_t>(ptr), num_elements * sizeof(T)) { /* ... */ }
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE T *GetPointer() const {
|
constexpr ALWAYS_INLINE T *GetPointer() const {
|
||||||
|
|
|
@ -57,7 +57,7 @@ namespace ams::tipc {
|
||||||
public:
|
public:
|
||||||
constexpr ObjectManagerBase() = default;
|
constexpr ObjectManagerBase() = default;
|
||||||
|
|
||||||
void Initialize(os::WaitableManagerType *manager, Entry *entries, size_t max_objects) {
|
void InitializeImpl(os::WaitableManagerType *manager, Entry *entries, size_t max_objects) {
|
||||||
/* Set our waitable manager. */
|
/* Set our waitable manager. */
|
||||||
m_waitable_manager = manager;
|
m_waitable_manager = manager;
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ namespace ams::tipc {
|
||||||
constexpr ObjectManager() = default;
|
constexpr ObjectManager() = default;
|
||||||
|
|
||||||
void Initialize(os::WaitableManagerType *manager) {
|
void Initialize(os::WaitableManagerType *manager) {
|
||||||
this->Initialize(manager, m_entries_storage, MaxObjects);
|
this->InitializeImpl(manager, m_entries_storage, MaxObjects);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -237,13 +237,15 @@ namespace ams::tipc {
|
||||||
--m_num_sessions;
|
--m_num_sessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartRegisterRetry(ResumeKey key) {
|
Result StartRegisterRetry(ResumeKey key) {
|
||||||
if constexpr (IsDeferralSupported) {
|
if constexpr (IsDeferralSupported) {
|
||||||
/* Acquire exclusive server manager access. */
|
/* Acquire exclusive server manager access. */
|
||||||
std::scoped_lock lk(m_server_manager->GetMutex());
|
std::scoped_lock lk(m_server_manager->GetMutex());
|
||||||
|
|
||||||
/* Begin the retry. */
|
/* Begin the retry. */
|
||||||
m_deferral_manager.StartRegisterRetry(key);
|
return m_deferral_manager.StartRegisterRetry(key);
|
||||||
|
} else {
|
||||||
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,7 +331,7 @@ namespace ams::tipc {
|
||||||
this->InitializeBase(id, sm, std::addressof(m_object_manager_impl));
|
this->InitializeBase(id, sm, std::addressof(m_object_manager_impl));
|
||||||
|
|
||||||
/* Initialize our object manager. */
|
/* Initialize our object manager. */
|
||||||
m_object_manager_impl->Initialize(std::addressof(this->m_waitable_manager));
|
m_object_manager_impl.Initialize(std::addressof(this->m_waitable_manager));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -430,6 +432,16 @@ namespace ams::tipc {
|
||||||
AMS_ABORT_UNLESS(allocated != nullptr);
|
AMS_ABORT_UNLESS(allocated != nullptr);
|
||||||
return allocated;
|
return allocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TriggerResume(ResumeKey resume_key) {
|
||||||
|
/* Acquire exclusive access to ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Check/trigger resume on each of our ports. */
|
||||||
|
[this, resume_key]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
|
||||||
|
(this->TriggerResumeImpl<Ix>(resume_key), ...);
|
||||||
|
}(std::make_index_sequence<NumPorts>());
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
template<size_t Ix> requires (Ix < NumPorts)
|
template<size_t Ix> requires (Ix < NumPorts)
|
||||||
void TryAllocateObject(size_t port_index, tipc::ServiceObjectBase *&allocated) {
|
void TryAllocateObject(size_t port_index, tipc::ServiceObjectBase *&allocated) {
|
||||||
|
@ -570,6 +582,17 @@ namespace ams::tipc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
void TriggerResumeImpl(ResumeKey resume_key) {
|
||||||
|
/* Get the port manager. */
|
||||||
|
auto &port_manager = this->GetPortManager<Ix>();
|
||||||
|
|
||||||
|
/* If we should, trigger a resume. */
|
||||||
|
if (port_manager.TestResume(resume_key)) {
|
||||||
|
port_manager.TriggerResume(resume_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename DeferralManagerType, typename... PortInfos>
|
template<typename DeferralManagerType, typename... PortInfos>
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
|
|
||||||
#define AMS_TEST_I_USER_INTERFACE_INTERFACE_INFO(C, H) \
|
|
||||||
AMS_TIPC_METHOD_INFO(C, H, 0, Result, RegisterClient, (const tipc::ClientProcessId &client_process_id), (client_process_id)) \
|
|
||||||
AMS_TIPC_METHOD_INFO(C, H, 1, Result, GetServiceHandle, (tipc::OutMoveHandle out_h, sm::ServiceName service), (out_h, service)) \
|
|
||||||
AMS_TIPC_METHOD_INFO(C, H, 2, Result, RegisterService, (tipc::OutMoveHandle out_h, sm::ServiceName service, u32 max_sessions, bool is_light), (out_h, service, max_sessions, is_light)) \
|
|
||||||
AMS_TIPC_METHOD_INFO(C, H, 3, Result, UnregisterService, (sm::ServiceName service), (service))
|
|
||||||
|
|
||||||
AMS_TIPC_DEFINE_INTERFACE(ams::_test::impl, IUserInterface, AMS_TEST_I_USER_INTERFACE_INTERFACE_INFO)
|
|
||||||
|
|
||||||
#define AMS_TEST_I_MANAGER_INTERFACE_INTERFACE_INFO(C, H) \
|
|
||||||
AMS_TIPC_METHOD_INFO(C, H, 0, Result, RegisterProcess, (os::ProcessId process_id, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac), (process_id, acid_sac, aci_sac)) \
|
|
||||||
AMS_TIPC_METHOD_INFO(C, H, 1, Result, UnregisterProcess, (os::ProcessId process_id), (process_id))
|
|
||||||
|
|
||||||
AMS_TIPC_DEFINE_INTERFACE(ams::_test::impl, IManagerInterface, AMS_TEST_I_MANAGER_INTERFACE_INTERFACE_INFO)
|
|
||||||
|
|
||||||
|
|
||||||
namespace ams::_test {
|
|
||||||
|
|
||||||
class UserInterfaceFacade {
|
|
||||||
public:
|
|
||||||
Result RegisterClient(const tipc::ClientProcessId &process_id);
|
|
||||||
Result GetServiceHandle(tipc::OutMoveHandle out_h, sm::ServiceName service);
|
|
||||||
Result RegisterService(tipc::OutMoveHandle out_h, sm::ServiceName service, u32 max_sessions, bool is_light);
|
|
||||||
Result UnregisterService(sm::ServiceName service);
|
|
||||||
|
|
||||||
Result ProcessDefaultServiceCommand(const svc::ipc::MessageBuffer &message_buffer);
|
|
||||||
};
|
|
||||||
static_assert(impl::IsIUserInterface<UserInterfaceFacade>);
|
|
||||||
|
|
||||||
class ManagerInterfaceFacade {
|
|
||||||
public:
|
|
||||||
Result RegisterProcess(os::ProcessId process_id, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac);
|
|
||||||
Result UnregisterProcess(os::ProcessId process_id);
|
|
||||||
};
|
|
||||||
static_assert(impl::IsIManagerInterface<ManagerInterfaceFacade>);
|
|
||||||
|
|
||||||
using UserInterfaceObject = ::ams::tipc::ServiceObject<impl::IUserInterface, UserInterfaceFacade>;
|
|
||||||
|
|
||||||
using ManagerInterfaceObject = ::ams::tipc::ServiceObject<impl::IManagerInterface, ManagerInterfaceFacade>;
|
|
||||||
|
|
||||||
Result TestAutomaticDispatch(UserInterfaceObject *object) {
|
|
||||||
return object->ProcessRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result TestManagerDispatch(ManagerInterfaceObject *object) {
|
|
||||||
return object->ProcessRequest();
|
|
||||||
}
|
|
||||||
|
|
||||||
using UserPortMeta = tipc::PortMeta<69, impl::IUserInterface, UserInterfaceFacade, tipc::SlabAllocator>;
|
|
||||||
using ManagerPortMeta = tipc::PortMeta< 1, impl::IManagerInterface, ManagerInterfaceFacade, tipc::SingletonAllocator>;
|
|
||||||
|
|
||||||
using TestServerManager = tipc::ServerManager<ManagerPortMeta, UserPortMeta>;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
TestServerManager g_test_server_manager;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestLoop() {
|
|
||||||
g_test_server_manager.LoopAuto();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -14,8 +14,9 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "impl/os_waitable_object_list.hpp"
|
|
||||||
#include "impl/os_timeout_helper.hpp"
|
#include "impl/os_timeout_helper.hpp"
|
||||||
|
#include "impl/os_waitable_object_list.hpp"
|
||||||
|
#include "impl/os_waitable_holder_impl.hpp"
|
||||||
|
|
||||||
namespace ams::os {
|
namespace ams::os {
|
||||||
|
|
||||||
|
@ -399,4 +400,20 @@ namespace ams::os {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InitializeWaitableHolder(WaitableHolderType *waitable_holder, MessageQueueType *mq, MessageQueueWaitType type) {
|
||||||
|
AMS_ASSERT(mq->state == MessageQueueType::State_Initialized);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case MessageQueueWaitType::ForNotFull:
|
||||||
|
util::ConstructAt(GetReference(waitable_holder->impl_storage).holder_of_mq_for_not_full_storage, mq);
|
||||||
|
break;
|
||||||
|
case MessageQueueWaitType::ForNotEmpty:
|
||||||
|
util::ConstructAt(GetReference(waitable_holder->impl_storage).holder_of_mq_for_not_empty_storage, mq);
|
||||||
|
break;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
|
||||||
|
waitable_holder->user_data = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
namespace ams::settings::impl {
|
namespace ams::settings::impl {
|
||||||
|
|
||||||
Result GetProductModel(s32 *out) {
|
Result GetProductModel(s32 *out) {
|
||||||
return ::setsysGetProductModel(out);
|
static_assert(sizeof(*out) == sizeof(::SetSysProductModel));
|
||||||
|
return ::setsysGetProductModel(reinterpret_cast<::SetSysProductModel *>(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,6 +386,10 @@ namespace ams::svc::ipc {
|
||||||
constexpr ALWAYS_INLINE MessageBuffer(u32 *b, size_t sz) : buffer(b), size(sz) { /* ... */ }
|
constexpr ALWAYS_INLINE MessageBuffer(u32 *b, size_t sz) : buffer(b), size(sz) { /* ... */ }
|
||||||
constexpr explicit ALWAYS_INLINE MessageBuffer(u32 *b) : buffer(b), size(sizeof(::ams::svc::ThreadLocalRegion::message_buffer)) { /* ... */ }
|
constexpr explicit ALWAYS_INLINE MessageBuffer(u32 *b) : buffer(b), size(sizeof(::ams::svc::ThreadLocalRegion::message_buffer)) { /* ... */ }
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE void *GetBufferForDebug() const {
|
||||||
|
return this->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE size_t GetBufferSize() const {
|
constexpr ALWAYS_INLINE size_t GetBufferSize() const {
|
||||||
return this->size;
|
return this->size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,19 +15,21 @@
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "sm_service_manager.hpp"
|
#include "sm_service_manager.hpp"
|
||||||
#include "sm_wait_list.hpp"
|
#include "../sm_wait_list.hpp"
|
||||||
|
|
||||||
namespace ams::sm::impl {
|
namespace ams::sm::impl {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
/* Constexpr definitions. */
|
/* Constexpr definitions. */
|
||||||
static constexpr size_t ProcessCountMax = 0x40;
|
static constexpr size_t ProcessCountMax = 0x50;
|
||||||
static constexpr size_t ServiceCountMax = 0x100 + 0x10; /* Extra 0x10 services over Nintendo for homebrew. */
|
static constexpr size_t ServiceCountMax = 0x100 + 0x10; /* Extra 0x10 services over Nintendo for homebrew. */
|
||||||
static constexpr size_t FutureMitmCountMax = 0x20;
|
static constexpr size_t FutureMitmCountMax = 0x20;
|
||||||
static constexpr size_t AccessControlSizeMax = 0x200;
|
static constexpr size_t AccessControlSizeMax = 0x200;
|
||||||
|
|
||||||
constexpr auto InitiallyDeferredServiceName = ServiceName::Encode("fsp-srv");
|
constexpr sm::ServiceName InitiallyDeferredServices[] = {
|
||||||
|
ServiceName::Encode("fsp-srv")
|
||||||
|
};
|
||||||
|
|
||||||
/* Types. */
|
/* Types. */
|
||||||
struct ProcessInfo {
|
struct ProcessInfo {
|
||||||
|
@ -36,86 +38,55 @@ namespace ams::sm::impl {
|
||||||
cfg::OverrideStatus override_status;
|
cfg::OverrideStatus override_status;
|
||||||
size_t access_control_size;
|
size_t access_control_size;
|
||||||
u8 access_control[AccessControlSizeMax];
|
u8 access_control[AccessControlSizeMax];
|
||||||
|
|
||||||
ProcessInfo() {
|
|
||||||
this->Free();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Free() {
|
|
||||||
this->process_id = os::InvalidProcessId;
|
|
||||||
this->program_id = ncm::InvalidProgramId;
|
|
||||||
this->override_status = {};
|
|
||||||
this->access_control_size = 0;
|
|
||||||
std::memset(this->access_control, 0, sizeof(this->access_control));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Forward declaration, for use in ServiceInfo. */
|
constexpr ProcessInfo InvalidProcessInfo = {
|
||||||
void GetMitmProcessInfo(MitmProcessInfo *out, os::ProcessId process_id);
|
.process_id = os::InvalidProcessId,
|
||||||
|
.program_id = ncm::InvalidProgramId,
|
||||||
|
.override_status = {},
|
||||||
|
.access_control_size = 0,
|
||||||
|
.access_control = {},
|
||||||
|
};
|
||||||
|
|
||||||
struct ServiceInfo {
|
struct ServiceInfo {
|
||||||
ServiceName name;
|
ServiceName name;
|
||||||
os::ProcessId owner_process_id;
|
os::ProcessId owner_process_id;
|
||||||
os::ProcessId mitm_process_id;
|
os::ProcessId mitm_process_id;
|
||||||
os::ProcessId mitm_waiting_ack_process_id;
|
os::ProcessId mitm_waiting_ack_process_id;
|
||||||
os::ManagedHandle mitm_port_h;
|
svc::Handle mitm_port_h;
|
||||||
os::ManagedHandle mitm_query_h;
|
svc::Handle mitm_query_h;
|
||||||
os::ManagedHandle port_h;
|
svc::Handle port_h;
|
||||||
os::ManagedHandle mitm_fwd_sess_h;
|
svc::Handle mitm_fwd_sess_h;
|
||||||
s32 max_sessions;
|
s32 max_sessions;
|
||||||
bool is_light;
|
bool is_light;
|
||||||
bool mitm_waiting_ack;
|
bool mitm_waiting_ack;
|
||||||
|
};
|
||||||
|
|
||||||
ServiceInfo() {
|
constexpr ServiceInfo InvalidServiceInfo = {
|
||||||
this->Free();
|
.name = sm::InvalidServiceName,
|
||||||
}
|
.owner_process_id = os::InvalidProcessId,
|
||||||
|
.mitm_process_id = os::InvalidProcessId,
|
||||||
void Free() {
|
.mitm_waiting_ack_process_id = os::InvalidProcessId,
|
||||||
/* Close any open handles. */
|
.mitm_port_h = svc::InvalidHandle,
|
||||||
this->port_h.Clear();
|
.mitm_query_h = svc::InvalidHandle,
|
||||||
this->mitm_port_h.Clear();
|
.port_h = svc::InvalidHandle,
|
||||||
this->mitm_query_h.Clear();
|
.mitm_fwd_sess_h = svc::InvalidHandle,
|
||||||
this->mitm_fwd_sess_h.Clear();
|
.max_sessions = 0,
|
||||||
|
.is_light = false,
|
||||||
/* Reset all other members. */
|
.mitm_waiting_ack = false,
|
||||||
this->name = InvalidServiceName;
|
|
||||||
this->owner_process_id = os::InvalidProcessId;
|
|
||||||
this->max_sessions = 0;
|
|
||||||
this->is_light = false;
|
|
||||||
this->mitm_process_id = os::InvalidProcessId;
|
|
||||||
this->mitm_waiting_ack = false;
|
|
||||||
this->mitm_waiting_ack_process_id = os::InvalidProcessId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FreeMitm() {
|
|
||||||
/* Close mitm handles. */
|
|
||||||
this->mitm_port_h.Clear();
|
|
||||||
this->mitm_query_h.Clear();
|
|
||||||
|
|
||||||
/* Reset mitm members. */
|
|
||||||
this->mitm_process_id = os::InvalidProcessId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AcknowledgeMitmSession(MitmProcessInfo *out_info, Handle *out_hnd) {
|
|
||||||
/* Copy to output. */
|
|
||||||
GetMitmProcessInfo(out_info, this->mitm_waiting_ack_process_id);
|
|
||||||
*out_hnd = this->mitm_fwd_sess_h.Move();
|
|
||||||
this->mitm_waiting_ack = false;
|
|
||||||
this->mitm_waiting_ack_process_id = os::InvalidProcessId;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AccessControlEntry {
|
class AccessControlEntry {
|
||||||
private:
|
private:
|
||||||
const u8 *entry;
|
const u8 *m_entry;
|
||||||
size_t capacity;
|
size_t m_capacity;
|
||||||
public:
|
public:
|
||||||
AccessControlEntry(const void *e, size_t c) : entry(reinterpret_cast<const u8 *>(e)), capacity(c) {
|
AccessControlEntry(const void *e, size_t c) : m_entry(static_cast<const u8 *>(e)), m_capacity(c) {
|
||||||
/* ... */
|
/* ... */
|
||||||
}
|
}
|
||||||
|
|
||||||
AccessControlEntry GetNextEntry() const {
|
AccessControlEntry GetNextEntry() const {
|
||||||
return AccessControlEntry(this->entry + this->GetSize(), this->capacity - this->GetSize());
|
return AccessControlEntry(m_entry + this->GetSize(), m_capacity - this->GetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetSize() const {
|
size_t GetSize() const {
|
||||||
|
@ -123,29 +94,29 @@ namespace ams::sm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetServiceNameSize() const {
|
size_t GetServiceNameSize() const {
|
||||||
return (this->entry[0] & 7) + 1;
|
return (m_entry[0] & 7) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceName GetServiceName() const {
|
ServiceName GetServiceName() const {
|
||||||
return ServiceName::Encode(reinterpret_cast<const char *>(this->entry + 1), this->GetServiceNameSize());
|
return ServiceName::Encode(reinterpret_cast<const char *>(m_entry + 1), this->GetServiceNameSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsHost() const {
|
bool IsHost() const {
|
||||||
return (this->entry[0] & 0x80) != 0;
|
return (m_entry[0] & 0x80) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsWildcard() const {
|
bool IsWildcard() const {
|
||||||
return this->entry[this->GetServiceNameSize()] == '*';
|
return m_entry[this->GetServiceNameSize()] == '*';
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValid() const {
|
bool IsValid() const {
|
||||||
/* Validate that we can access data. */
|
/* Validate that we can access data. */
|
||||||
if (this->entry == nullptr || this->capacity == 0) {
|
if (m_entry == nullptr || m_capacity == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate that the size is correct. */
|
/* Validate that the size is correct. */
|
||||||
return this->GetSize() <= this->capacity;
|
return this->GetSize() <= m_capacity;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,30 +140,51 @@ namespace ams::sm::impl {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Static members. */
|
/* Static members. */
|
||||||
ProcessInfo g_process_list[ProcessCountMax];
|
|
||||||
ServiceInfo g_service_list[ServiceCountMax];
|
/* NOTE: In 12.0.0, Nintendo added multithreaded processing to sm; however, official sm does not do */
|
||||||
ServiceName g_future_mitm_list[FutureMitmCountMax];
|
/* any kind of mutual exclusivity when accessing (and modifying) global state. Previously, this was */
|
||||||
|
/* not a problem, because sm was strictly single-threaded, and so two threads could not race eachother. */
|
||||||
|
/* We will add a mutex (and perform locking) in order to prevent simultaneous access to global state. */
|
||||||
|
constinit os::Mutex g_mutex{true};
|
||||||
|
|
||||||
|
constinit std::array<ProcessInfo, ProcessCountMax> g_process_list = [] {
|
||||||
|
std::array<ProcessInfo, ProcessCountMax> list = {};
|
||||||
|
|
||||||
|
/* Initialize each info. */
|
||||||
|
for (auto &process_info : list) {
|
||||||
|
process_info = InvalidProcessInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}();
|
||||||
|
|
||||||
|
constinit std::array<ServiceInfo, ServiceCountMax> g_service_list = [] {
|
||||||
|
std::array<ServiceInfo, ServiceCountMax> list = {};
|
||||||
|
|
||||||
|
/* Initialize each info. */
|
||||||
|
for (auto &service_info : list) {
|
||||||
|
service_info = InvalidServiceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}();
|
||||||
|
|
||||||
|
constinit std::array<ServiceName, FutureMitmCountMax> g_future_mitm_list = [] {
|
||||||
|
std::array<ServiceName, FutureMitmCountMax> list = {};
|
||||||
|
|
||||||
|
/* Initialize each info. */
|
||||||
|
for (auto &name : list) {
|
||||||
|
name = InvalidServiceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}();
|
||||||
|
|
||||||
|
constinit bool g_ended_initial_defers = false;
|
||||||
|
|
||||||
InitialProcessIdLimits g_initial_process_id_limits;
|
InitialProcessIdLimits g_initial_process_id_limits;
|
||||||
bool g_ended_initial_defers;
|
|
||||||
|
|
||||||
/* Helper functions for interacting with processes/services. */
|
|
||||||
ProcessInfo *GetProcessInfo(os::ProcessId process_id) {
|
|
||||||
for (size_t i = 0; i < ProcessCountMax; i++) {
|
|
||||||
if (g_process_list[i].process_id == process_id) {
|
|
||||||
return &g_process_list[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessInfo *GetFreeProcessInfo() {
|
|
||||||
return GetProcessInfo(os::InvalidProcessId);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasProcessInfo(os::ProcessId process_id) {
|
|
||||||
return GetProcessInfo(process_id) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Helper functionality. */
|
||||||
bool IsInitialProcess(os::ProcessId process_id) {
|
bool IsInitialProcess(os::ProcessId process_id) {
|
||||||
return g_initial_process_id_limits.IsInitialProcess(process_id);
|
return g_initial_process_id_limits.IsInitialProcess(process_id);
|
||||||
}
|
}
|
||||||
|
@ -201,91 +193,6 @@ namespace ams::sm::impl {
|
||||||
return process_id != os::InvalidProcessId;
|
return process_id != os::InvalidProcessId;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceInfo *GetServiceInfo(ServiceName service_name) {
|
|
||||||
for (size_t i = 0; i < ServiceCountMax; i++) {
|
|
||||||
if (g_service_list[i].name == service_name) {
|
|
||||||
return &g_service_list[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceInfo *GetFreeServiceInfo() {
|
|
||||||
return GetServiceInfo(InvalidServiceName);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasServiceInfo(ServiceName service) {
|
|
||||||
return GetServiceInfo(service) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasMitm(ServiceName service) {
|
|
||||||
const ServiceInfo *service_info = GetServiceInfo(service);
|
|
||||||
return service_info != nullptr && IsValidProcessId(service_info->mitm_process_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetMitmProcessInfo(MitmProcessInfo *out_info, os::ProcessId process_id) {
|
|
||||||
/* Anything that can request a mitm session must have a process info. */
|
|
||||||
const auto process_info = GetProcessInfo(process_id);
|
|
||||||
AMS_ABORT_UNLESS(process_info != nullptr);
|
|
||||||
|
|
||||||
/* Write to output. */
|
|
||||||
out_info->process_id = process_id;
|
|
||||||
out_info->program_id = process_info->program_id;
|
|
||||||
out_info->override_status = process_info->override_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsMitmDisallowed(ncm::ProgramId program_id) {
|
|
||||||
/* Mitm used on certain programs can prevent the boot process from completing. */
|
|
||||||
/* TODO: Is there a way to do this that's less hardcoded? Needs design thought. */
|
|
||||||
return program_id == ncm::SystemProgramId::Loader ||
|
|
||||||
program_id == ncm::SystemProgramId::Pm ||
|
|
||||||
program_id == ncm::SystemProgramId::Spl ||
|
|
||||||
program_id == ncm::SystemProgramId::Boot ||
|
|
||||||
program_id == ncm::SystemProgramId::Ncm ||
|
|
||||||
program_id == ncm::AtmosphereProgramId::Mitm ||
|
|
||||||
program_id == ncm::SystemProgramId::Creport;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result AddFutureMitmDeclaration(ServiceName service) {
|
|
||||||
for (size_t i = 0; i < FutureMitmCountMax; i++) {
|
|
||||||
if (g_future_mitm_list[i] == InvalidServiceName) {
|
|
||||||
g_future_mitm_list[i] = service;
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sm::ResultOutOfServices();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasFutureMitmDeclaration(ServiceName service) {
|
|
||||||
for (size_t i = 0; i < FutureMitmCountMax; i++) {
|
|
||||||
if (g_future_mitm_list[i] == service) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearFutureMitmDeclaration(ServiceName service) {
|
|
||||||
for (size_t i = 0; i < FutureMitmCountMax; i++) {
|
|
||||||
if (g_future_mitm_list[i] == service) {
|
|
||||||
g_future_mitm_list[i] = InvalidServiceName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This might undefer some requests. */
|
|
||||||
TriggerResume(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetServiceInfoRecord(ServiceRecord *out_record, const ServiceInfo *service_info) {
|
|
||||||
out_record->service = service_info->name;
|
|
||||||
out_record->owner_process_id = service_info->owner_process_id;
|
|
||||||
out_record->max_sessions = service_info->max_sessions;
|
|
||||||
out_record->mitm_process_id = service_info->mitm_process_id;
|
|
||||||
out_record->mitm_waiting_ack_process_id = service_info->mitm_waiting_ack_process_id;
|
|
||||||
out_record->is_light = service_info->is_light;
|
|
||||||
out_record->mitm_waiting_ack = service_info->mitm_waiting_ack;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ValidateAccessControl(AccessControlEntry access_control, ServiceName service, bool is_host, bool is_wildcard) {
|
Result ValidateAccessControl(AccessControlEntry access_control, ServiceName service, bool is_host, bool is_wildcard) {
|
||||||
/* Iterate over all entries in the access control, checking to see if we have a match. */
|
/* Iterate over all entries in the access control, checking to see if we have a match. */
|
||||||
while (access_control.IsValid()) {
|
while (access_control.IsValid()) {
|
||||||
|
@ -298,7 +205,7 @@ namespace ams::sm::impl {
|
||||||
} else if (access_control.IsWildcard()) {
|
} else if (access_control.IsWildcard()) {
|
||||||
/* Also allow fuzzy match for wildcard. */
|
/* Also allow fuzzy match for wildcard. */
|
||||||
ServiceName ac_service = access_control.GetServiceName();
|
ServiceName ac_service = access_control.GetServiceName();
|
||||||
is_valid &= std::memcmp(&ac_service, &service, access_control.GetServiceNameSize() - 1) == 0;
|
is_valid &= std::memcmp(std::addressof(ac_service), std::addressof(service), access_control.GetServiceNameSize() - 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
R_SUCCEED_IF(is_valid);
|
R_SUCCEED_IF(is_valid);
|
||||||
|
@ -347,27 +254,144 @@ namespace ams::sm::impl {
|
||||||
|
|
||||||
/* This is a mechanism by which certain services will always be deferred until sm:m receives a special command. */
|
/* This is a mechanism by which certain services will always be deferred until sm:m receives a special command. */
|
||||||
/* This can be extended with more services as needed at a later date. */
|
/* This can be extended with more services as needed at a later date. */
|
||||||
return service == InitiallyDeferredServiceName;
|
for (const auto &service_name : InitiallyDeferredServices) {
|
||||||
|
if (service == service_name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetMitmServiceHandleImpl(Handle *out, ServiceInfo *service_info, const MitmProcessInfo &client_info) {
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessInfo *GetProcessInfo(os::ProcessId process_id) {
|
||||||
|
/* Find a process info with a matching id. */
|
||||||
|
for (auto &process_info : g_process_list) {
|
||||||
|
if (process_info.process_id == process_id) {
|
||||||
|
return std::addressof(process_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessInfo *GetFreeProcessInfo() {
|
||||||
|
return GetProcessInfo(os::InvalidProcessId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasProcessInfo(os::ProcessId process_id) {
|
||||||
|
return GetProcessInfo(process_id) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceInfo *GetServiceInfo(ServiceName service_name) {
|
||||||
|
/* Find a service with a matching name. */
|
||||||
|
for (auto &service_info : g_service_list) {
|
||||||
|
if (service_info.name == service_name) {
|
||||||
|
return std::addressof(service_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceInfo *GetFreeServiceInfo() {
|
||||||
|
return GetServiceInfo(InvalidServiceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasServiceInfo(ServiceName service) {
|
||||||
|
return GetServiceInfo(service) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasMitm(ServiceName service) {
|
||||||
|
const ServiceInfo *service_info = GetServiceInfo(service);
|
||||||
|
return service_info != nullptr && IsValidProcessId(service_info->mitm_process_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AddFutureMitmDeclaration(ServiceName service) {
|
||||||
|
for (auto &future_mitm : g_future_mitm_list) {
|
||||||
|
if (future_mitm == InvalidServiceName) {
|
||||||
|
future_mitm = service;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sm::ResultOutOfServices();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasFutureMitmDeclaration(ServiceName service) {
|
||||||
|
for (const auto &future_mitm : g_future_mitm_list){
|
||||||
|
if (future_mitm == service) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearFutureMitmDeclaration(ServiceName service) {
|
||||||
|
for (auto &future_mitm : g_future_mitm_list) {
|
||||||
|
if (future_mitm == service) {
|
||||||
|
future_mitm = InvalidServiceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This might undefer some requests. */
|
||||||
|
TriggerResume(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetMitmProcessInfo(MitmProcessInfo *out_info, os::ProcessId process_id) {
|
||||||
|
/* Anything that can request a mitm session must have a process info. */
|
||||||
|
const auto process_info = GetProcessInfo(process_id);
|
||||||
|
AMS_ABORT_UNLESS(process_info != nullptr);
|
||||||
|
|
||||||
|
/* Write to output. */
|
||||||
|
out_info->process_id = process_id;
|
||||||
|
out_info->program_id = process_info->program_id;
|
||||||
|
out_info->override_status = process_info->override_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsMitmDisallowed(ncm::ProgramId program_id) {
|
||||||
|
/* Mitm used on certain programs can prevent the boot process from completing. */
|
||||||
|
/* TODO: Is there a way to do this that's less hardcoded? Needs design thought. */
|
||||||
|
return program_id == ncm::SystemProgramId::Loader ||
|
||||||
|
program_id == ncm::SystemProgramId::Pm ||
|
||||||
|
program_id == ncm::SystemProgramId::Spl ||
|
||||||
|
program_id == ncm::SystemProgramId::Boot ||
|
||||||
|
program_id == ncm::SystemProgramId::Ncm ||
|
||||||
|
program_id == ncm::AtmosphereProgramId::Mitm ||
|
||||||
|
program_id == ncm::SystemProgramId::Creport;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result GetMitmServiceHandleImpl(svc::Handle *out, ServiceInfo *service_info, const MitmProcessInfo &client_info) {
|
||||||
/* Send command to query if we should mitm. */
|
/* Send command to query if we should mitm. */
|
||||||
bool should_mitm;
|
bool should_mitm;
|
||||||
{
|
{
|
||||||
Service srv { .session = service_info->mitm_query_h.Get() };
|
/* TODO: Convert mitm internal messaging to use tipc? */
|
||||||
R_TRY(serviceDispatchInOut(&srv, 65000, client_info, should_mitm));
|
::Service srv { .session = service_info->mitm_query_h };
|
||||||
|
R_TRY(::serviceDispatchInOut(std::addressof(srv), 65000, client_info, should_mitm));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we shouldn't mitm, give normal session. */
|
/* If we shouldn't mitm, give normal session. */
|
||||||
R_UNLESS(should_mitm, svcConnectToPort(out, service_info->port_h.Get()));
|
R_UNLESS(should_mitm, svc::ConnectToPort(out, service_info->port_h));
|
||||||
|
|
||||||
/* Create both handles. */
|
/* Create both handles. */
|
||||||
{
|
{
|
||||||
os::ManagedHandle fwd_hnd, hnd;
|
/* Get the forward handle. */
|
||||||
R_TRY(svcConnectToPort(fwd_hnd.GetPointer(), service_info->port_h.Get()));
|
svc::Handle fwd_hnd;
|
||||||
R_TRY(svcConnectToPort(hnd.GetPointer(), service_info->mitm_port_h.Get()));
|
R_TRY(svc::ConnectToPort(std::addressof(fwd_hnd), service_info->port_h));
|
||||||
service_info->mitm_fwd_sess_h = std::move(fwd_hnd);
|
|
||||||
*out = hnd.Move();
|
/* Ensure that the forward handle is closed, if we fail to get the mitm handle. */
|
||||||
|
auto fwd_guard = SCOPE_GUARD { R_ABORT_UNLESS(svc::CloseHandle(fwd_hnd)); };
|
||||||
|
|
||||||
|
/* Get the mitm handle. */
|
||||||
|
svc::Handle hnd;
|
||||||
|
R_TRY(svc::ConnectToPort(std::addressof(hnd), service_info->mitm_port_h));
|
||||||
|
|
||||||
|
/* We got both handles, so we no longer need to clean up the forward handle. */
|
||||||
|
fwd_guard.Cancel();
|
||||||
|
|
||||||
|
/* Save the handles to their respective storages. */
|
||||||
|
service_info->mitm_fwd_sess_h = fwd_hnd;
|
||||||
|
*out = hnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
service_info->mitm_waiting_ack_process_id = client_info.process_id;
|
service_info->mitm_waiting_ack_process_id = client_info.process_id;
|
||||||
|
@ -376,15 +400,15 @@ namespace ams::sm::impl {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetServiceHandleImpl(Handle *out, ServiceInfo *service_info, os::ProcessId process_id) {
|
Result GetServiceHandleImpl(svc::Handle *out, ServiceInfo *service_info, os::ProcessId process_id) {
|
||||||
/* Clear handle output. */
|
/* Clear handle output. */
|
||||||
*out = INVALID_HANDLE;
|
*out = svc::InvalidHandle;
|
||||||
|
|
||||||
/* Check if we should return a mitm handle. */
|
/* Check if we should return a mitm handle. */
|
||||||
if (IsValidProcessId(service_info->mitm_process_id) && service_info->mitm_process_id != process_id) {
|
if (IsValidProcessId(service_info->mitm_process_id) && service_info->mitm_process_id != process_id) {
|
||||||
/* Get mitm process info, ensure that we're allowed to mitm the given program. */
|
/* Get mitm process info, ensure that we're allowed to mitm the given program. */
|
||||||
MitmProcessInfo client_info;
|
MitmProcessInfo client_info;
|
||||||
GetMitmProcessInfo(&client_info, process_id);
|
GetMitmProcessInfo(std::addressof(client_info), process_id);
|
||||||
if (!IsMitmDisallowed(client_info.program_id)) {
|
if (!IsMitmDisallowed(client_info.program_id)) {
|
||||||
/* We're mitm'd. Assert, because mitm service host dead is an error state. */
|
/* We're mitm'd. Assert, because mitm service host dead is an error state. */
|
||||||
R_ABORT_UNLESS(GetMitmServiceHandleImpl(out, service_info, client_info));
|
R_ABORT_UNLESS(GetMitmServiceHandleImpl(out, service_info, client_info));
|
||||||
|
@ -393,10 +417,10 @@ namespace ams::sm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We're not returning a mitm handle, so just return a normal port handle. */
|
/* We're not returning a mitm handle, so just return a normal port handle. */
|
||||||
return svcConnectToPort(out, service_info->port_h.Get());
|
return svc::ConnectToPort(out, service_info->port_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result RegisterServiceImpl(Handle *out, os::ProcessId process_id, ServiceName service, size_t max_sessions, bool is_light) {
|
Result RegisterServiceImpl(svc::Handle *out, os::ProcessId process_id, ServiceName service, size_t max_sessions, bool is_light) {
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -408,16 +432,16 @@ namespace ams::sm::impl {
|
||||||
R_UNLESS(free_service != nullptr, sm::ResultOutOfServices());
|
R_UNLESS(free_service != nullptr, sm::ResultOutOfServices());
|
||||||
|
|
||||||
/* Create the new service. */
|
/* Create the new service. */
|
||||||
*out = INVALID_HANDLE;
|
*out = svc::InvalidHandle;
|
||||||
Handle server_hnd = INVALID_HANDLE;
|
svc::Handle server_hnd = svc::InvalidHandle;
|
||||||
R_TRY(svcCreatePort(out, std::addressof(server_hnd), max_sessions, is_light, free_service->name.name));
|
R_TRY(svc::CreatePort(out, std::addressof(server_hnd), max_sessions, is_light, reinterpret_cast<uintptr_t>(free_service->name.name)));
|
||||||
|
|
||||||
/* Save info. */
|
/* Save info. */
|
||||||
free_service->name = service;
|
free_service->name = service;
|
||||||
free_service->owner_process_id = process_id;
|
free_service->owner_process_id = process_id;
|
||||||
free_service->max_sessions = max_sessions;
|
free_service->max_sessions = max_sessions;
|
||||||
free_service->is_light = is_light;
|
free_service->is_light = is_light;
|
||||||
*free_service->port_h.GetPointerAndClear() = server_hnd;
|
free_service->port_h = server_hnd;
|
||||||
|
|
||||||
/* This might undefer some requests. */
|
/* This might undefer some requests. */
|
||||||
TriggerResume(service);
|
TriggerResume(service);
|
||||||
|
@ -425,25 +449,42 @@ namespace ams::sm::impl {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UnregisterServiceImpl(ServiceInfo *service_info) {
|
||||||
|
/* Close all valid handles. */
|
||||||
|
if (service_info->port_h != svc::InvalidHandle) { R_ABORT_UNLESS(svc::CloseHandle(service_info->port_h)); }
|
||||||
|
if (service_info->mitm_port_h != svc::InvalidHandle) { R_ABORT_UNLESS(svc::CloseHandle(service_info->mitm_port_h)); }
|
||||||
|
if (service_info->mitm_query_h != svc::InvalidHandle) { R_ABORT_UNLESS(svc::CloseHandle(service_info->mitm_query_h)); }
|
||||||
|
if (service_info->mitm_fwd_sess_h != svc::InvalidHandle) { R_ABORT_UNLESS(svc::CloseHandle(service_info->mitm_fwd_sess_h)); }
|
||||||
|
|
||||||
|
/* Reset the info's state. */
|
||||||
|
*service_info = InvalidServiceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Client disconnection callback. */
|
/* Client disconnection callback. */
|
||||||
void OnClientDisconnected(os::ProcessId process_id) {
|
void OnClientDisconnected(os::ProcessId process_id) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Ensure that the process id is valid. */
|
/* Ensure that the process id is valid. */
|
||||||
if (process_id == os::InvalidProcessId) {
|
if (process_id == os::InvalidProcessId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unregister all services a client hosts, on attached-client-close. */
|
/* Unregister all services a client hosts, on attached-client-close. */
|
||||||
for (size_t i = 0; i < ServiceCountMax; i++) {
|
for (auto &service_info : g_service_list) {
|
||||||
if (g_service_list[i].name != InvalidServiceName && g_service_list[i].owner_process_id == process_id) {
|
if (service_info.name != InvalidServiceName && service_info.owner_process_id == process_id) {
|
||||||
g_service_list[i].Free();
|
UnregisterServiceImpl(std::addressof(service_info));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Process management. */
|
/* Process management. */
|
||||||
Result RegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const void *acid_sac, size_t acid_sac_size, const void *aci_sac, size_t aci_sac_size) {
|
Result RegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const void *acid_sac, size_t acid_sac_size, const void *aci_sac, size_t aci_sac_size) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Check that access control will fit in the ServiceInfo. */
|
/* Check that access control will fit in the ServiceInfo. */
|
||||||
R_UNLESS(aci_sac_size <= AccessControlSizeMax, sm::ResultTooLargeAccessControl());
|
R_UNLESS(aci_sac_size <= AccessControlSizeMax, sm::ResultTooLargeAccessControl());
|
||||||
|
|
||||||
|
@ -461,20 +502,29 @@ namespace ams::sm::impl {
|
||||||
proc->override_status = override_status;
|
proc->override_status = override_status;
|
||||||
proc->access_control_size = aci_sac_size;
|
proc->access_control_size = aci_sac_size;
|
||||||
std::memcpy(proc->access_control, aci_sac, proc->access_control_size);
|
std::memcpy(proc->access_control, aci_sac, proc->access_control_size);
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UnregisterProcess(os::ProcessId process_id) {
|
Result UnregisterProcess(os::ProcessId process_id) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Find the process. */
|
/* Find the process. */
|
||||||
ProcessInfo *proc = GetProcessInfo(process_id);
|
ProcessInfo *proc = GetProcessInfo(process_id);
|
||||||
R_UNLESS(proc != nullptr, sm::ResultInvalidClient());
|
R_UNLESS(proc != nullptr, sm::ResultInvalidClient());
|
||||||
|
|
||||||
proc->Free();
|
/* Free the process. */
|
||||||
|
*proc = InvalidProcessInfo;
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Service management. */
|
/* Service management. */
|
||||||
Result HasService(bool *out, ServiceName service) {
|
Result HasService(bool *out, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -483,16 +533,24 @@ namespace ams::sm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result WaitService(ServiceName service) {
|
Result WaitService(ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
|
/* Check that we have the service. */
|
||||||
bool has_service = false;
|
bool has_service = false;
|
||||||
R_TRY(impl::HasService(&has_service, service));
|
R_TRY(impl::HasService(&has_service, service));
|
||||||
|
|
||||||
/* Wait until we have the service. */
|
/* If we do, we can succeed immediately. */
|
||||||
R_SUCCEED_IF(has_service);
|
R_SUCCEED_IF(has_service);
|
||||||
|
|
||||||
|
/* Otherwise, we want to wait until the service is registered. */
|
||||||
return StartRegisterRetry(service);
|
return StartRegisterRetry(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result GetServiceHandle(Handle *out, os::ProcessId process_id, ServiceName service) {
|
Result GetServiceHandle(svc::Handle *out, os::ProcessId process_id, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -525,7 +583,10 @@ namespace ams::sm::impl {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result RegisterService(Handle *out, os::ProcessId process_id, ServiceName service, size_t max_sessions, bool is_light) {
|
Result RegisterService(svc::Handle *out, os::ProcessId process_id, ServiceName service, size_t max_sessions, bool is_light) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -537,15 +598,23 @@ namespace ams::sm::impl {
|
||||||
R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, true, false));
|
R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, true, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check that the service isn't already registered. */
|
||||||
R_UNLESS(!HasServiceInfo(service), sm::ResultAlreadyRegistered());
|
R_UNLESS(!HasServiceInfo(service), sm::ResultAlreadyRegistered());
|
||||||
|
|
||||||
return RegisterServiceImpl(out, process_id, service, max_sessions, is_light);
|
return RegisterServiceImpl(out, process_id, service, max_sessions, is_light);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result RegisterServiceForSelf(Handle *out, ServiceName service, size_t max_sessions) {
|
Result RegisterServiceForSelf(svc::Handle *out, ServiceName service, size_t max_sessions) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
return RegisterServiceImpl(out, os::GetCurrentProcessId(), service, max_sessions, false);
|
return RegisterServiceImpl(out, os::GetCurrentProcessId(), service, max_sessions, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UnregisterService(os::ProcessId process_id, ServiceName service) {
|
Result UnregisterService(os::ProcessId process_id, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -562,31 +631,44 @@ namespace ams::sm::impl {
|
||||||
R_UNLESS(service_info->owner_process_id == process_id, sm::ResultNotAllowed());
|
R_UNLESS(service_info->owner_process_id == process_id, sm::ResultNotAllowed());
|
||||||
|
|
||||||
/* Unregister the service. */
|
/* Unregister the service. */
|
||||||
service_info->Free();
|
UnregisterServiceImpl(service_info);
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mitm extensions. */
|
/* Mitm extensions. */
|
||||||
Result HasMitm(bool *out, ServiceName service) {
|
Result HasMitm(bool *out, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
const ServiceInfo *service_info = GetServiceInfo(service);
|
/* Get whether we have a mitm. */
|
||||||
*out = service_info != nullptr && IsValidProcessId(service_info->mitm_process_id);
|
*out = HasMitm(service);
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result WaitMitm(ServiceName service) {
|
Result WaitMitm(ServiceName service) {
|
||||||
bool has_mitm = false;
|
/* Acquire exclusive access to global state. */
|
||||||
R_TRY(impl::HasMitm(&has_mitm, service));
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Wait until we have the mitm. */
|
/* Check that we have the mitm. */
|
||||||
|
bool has_mitm = false;
|
||||||
|
R_TRY(impl::HasMitm(std::addressof(has_mitm), service));
|
||||||
|
|
||||||
|
/* If we do, we can succeed immediately. */
|
||||||
R_SUCCEED_IF(has_mitm);
|
R_SUCCEED_IF(has_mitm);
|
||||||
|
|
||||||
|
/* Otherwise, we want to wait until the service is registered. */
|
||||||
return StartRegisterRetry(service);
|
return StartRegisterRetry(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result InstallMitm(Handle *out, Handle *out_query, os::ProcessId process_id, ServiceName service) {
|
Result InstallMitm(svc::Handle *out, svc::Handle *out_query, os::ProcessId process_id, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -601,20 +683,18 @@ namespace ams::sm::impl {
|
||||||
ServiceInfo *service_info = GetServiceInfo(service);
|
ServiceInfo *service_info = GetServiceInfo(service);
|
||||||
|
|
||||||
/* If it doesn't exist, defer until it does. */
|
/* If it doesn't exist, defer until it does. */
|
||||||
if (service_info == nullptr) {
|
R_UNLESS(service_info != nullptr, StartRegisterRetry(service));
|
||||||
return StartRegisterRetry(service);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate that the service isn't already being mitm'd. */
|
/* Validate that the service isn't already being mitm'd. */
|
||||||
R_UNLESS(!IsValidProcessId(service_info->mitm_process_id), sm::ResultAlreadyRegistered());
|
R_UNLESS(!IsValidProcessId(service_info->mitm_process_id), sm::ResultAlreadyRegistered());
|
||||||
|
|
||||||
/* Always clear output. */
|
/* Always clear output. */
|
||||||
*out = INVALID_HANDLE;
|
*out = svc::InvalidHandle;
|
||||||
*out_query = INVALID_HANDLE;
|
*out_query = svc::InvalidHandle;
|
||||||
|
|
||||||
/* If we don't have a future mitm declaration, add one. */
|
/* If we don't have a future mitm declaration, add one. */
|
||||||
/* Client will clear this when ready to process. */
|
/* Client will clear this when ready to process. */
|
||||||
bool has_existing_future_declaration = HasFutureMitmDeclaration(service);
|
const bool has_existing_future_declaration = HasFutureMitmDeclaration(service);
|
||||||
if (!has_existing_future_declaration) {
|
if (!has_existing_future_declaration) {
|
||||||
R_TRY(AddFutureMitmDeclaration(service));
|
R_TRY(AddFutureMitmDeclaration(service));
|
||||||
}
|
}
|
||||||
|
@ -623,16 +703,26 @@ namespace ams::sm::impl {
|
||||||
|
|
||||||
/* Create mitm handles. */
|
/* Create mitm handles. */
|
||||||
{
|
{
|
||||||
os::ManagedHandle hnd, port_hnd, qry_hnd, mitm_qry_hnd;
|
/* Get the port handles. */
|
||||||
R_TRY(svcCreatePort(hnd.GetPointer(), port_hnd.GetPointer(), service_info->max_sessions, service_info->is_light, service_info->name.name));
|
svc::Handle hnd, port_hnd;
|
||||||
R_TRY(svcCreateSession(qry_hnd.GetPointer(), mitm_qry_hnd.GetPointer(), 0, 0));
|
R_TRY(svc::CreatePort(std::addressof(hnd), std::addressof(port_hnd), service_info->max_sessions, service_info->is_light, reinterpret_cast<uintptr_t>(service_info->name.name)));
|
||||||
|
|
||||||
|
/* Ensure that we clean up the port handles, if something goes wrong creating the query sessions. */
|
||||||
|
auto port_guard = SCOPE_GUARD { R_ABORT_UNLESS(svc::CloseHandle(hnd)); R_ABORT_UNLESS(svc::CloseHandle(port_hnd)); };
|
||||||
|
|
||||||
|
/* Create the session for our query service. */
|
||||||
|
svc::Handle qry_hnd, mitm_qry_hnd;
|
||||||
|
R_TRY(svc::CreateSession(std::addressof(qry_hnd), std::addressof(mitm_qry_hnd), false, 0));
|
||||||
|
|
||||||
|
/* We created the query service session, so we no longer need to clean up the port handles. */
|
||||||
|
port_guard.Cancel();
|
||||||
|
|
||||||
/* Copy to output. */
|
/* Copy to output. */
|
||||||
service_info->mitm_process_id = process_id;
|
service_info->mitm_process_id = process_id;
|
||||||
service_info->mitm_port_h = std::move(port_hnd);
|
service_info->mitm_port_h = port_hnd;
|
||||||
service_info->mitm_query_h = std::move(mitm_qry_hnd);
|
service_info->mitm_query_h = mitm_qry_hnd;
|
||||||
*out = hnd.Move();
|
*out = hnd;
|
||||||
*out_query = qry_hnd.Move();
|
*out_query = qry_hnd;
|
||||||
|
|
||||||
/* This might undefer some requests. */
|
/* This might undefer some requests. */
|
||||||
TriggerResume(service);
|
TriggerResume(service);
|
||||||
|
@ -643,6 +733,9 @@ namespace ams::sm::impl {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UninstallMitm(os::ProcessId process_id, ServiceName service) {
|
Result UninstallMitm(os::ProcessId process_id, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -659,12 +752,26 @@ namespace ams::sm::impl {
|
||||||
/* Validate that the client process_id is the mitm process. */
|
/* Validate that the client process_id is the mitm process. */
|
||||||
R_UNLESS(service_info->mitm_process_id == process_id, sm::ResultNotAllowed());
|
R_UNLESS(service_info->mitm_process_id == process_id, sm::ResultNotAllowed());
|
||||||
|
|
||||||
/* Free Mitm session info. */
|
/* Uninstall the mitm. */
|
||||||
service_info->FreeMitm();
|
{
|
||||||
|
/* Close mitm handles. */
|
||||||
|
R_ABORT_UNLESS(svc::CloseHandle(service_info->mitm_port_h));
|
||||||
|
R_ABORT_UNLESS(svc::CloseHandle(service_info->mitm_query_h));
|
||||||
|
|
||||||
|
/* Reset mitm members. */
|
||||||
|
service_info->mitm_port_h = svc::InvalidHandle;
|
||||||
|
service_info->mitm_query_h = svc::InvalidHandle;
|
||||||
|
service_info->mitm_process_id = os::InvalidProcessId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result DeclareFutureMitm(os::ProcessId process_id, ServiceName service) {
|
Result DeclareFutureMitm(os::ProcessId process_id, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -681,10 +788,14 @@ namespace ams::sm::impl {
|
||||||
|
|
||||||
/* Try to forward declare it. */
|
/* Try to forward declare it. */
|
||||||
R_TRY(AddFutureMitmDeclaration(service));
|
R_TRY(AddFutureMitmDeclaration(service));
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ClearFutureMitm(os::ProcessId process_id, ServiceName service) {
|
Result ClearFutureMitm(os::ProcessId process_id, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -709,10 +820,14 @@ namespace ams::sm::impl {
|
||||||
|
|
||||||
/* Clear the forward declaration. */
|
/* Clear the forward declaration. */
|
||||||
ClearFutureMitmDeclaration(service);
|
ClearFutureMitmDeclaration(service);
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result AcknowledgeMitmSession(MitmProcessInfo *out_info, Handle *out_hnd, os::ProcessId process_id, ServiceName service) {
|
Result AcknowledgeMitmSession(MitmProcessInfo *out_info, svc::Handle *out_hnd, os::ProcessId process_id, ServiceName service) {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
/* Validate service name. */
|
/* Validate service name. */
|
||||||
R_TRY(ValidateServiceName(service));
|
R_TRY(ValidateServiceName(service));
|
||||||
|
|
||||||
|
@ -731,7 +846,18 @@ namespace ams::sm::impl {
|
||||||
R_UNLESS(service_info->mitm_waiting_ack, sm::ResultNotAllowed());
|
R_UNLESS(service_info->mitm_waiting_ack, sm::ResultNotAllowed());
|
||||||
|
|
||||||
/* Acknowledge. */
|
/* Acknowledge. */
|
||||||
service_info->AcknowledgeMitmSession(out_info, out_hnd);
|
{
|
||||||
|
/* Copy the mitm info to output. */
|
||||||
|
GetMitmProcessInfo(out_info, service_info->mitm_waiting_ack_process_id);
|
||||||
|
|
||||||
|
/* Set the output handle. */
|
||||||
|
*out_hnd = service_info->mitm_fwd_sess_h;
|
||||||
|
service_info->mitm_fwd_sess_h = svc::InvalidHandle;
|
||||||
|
|
||||||
|
/* Clear acknowledgement-related fields. */
|
||||||
|
service_info->mitm_waiting_ack = false;
|
||||||
|
service_info->mitm_waiting_ack_process_id = os::InvalidProcessId;
|
||||||
|
}
|
||||||
|
|
||||||
/* Undefer requests to the session. */
|
/* Undefer requests to the session. */
|
||||||
TriggerResume(service);
|
TriggerResume(service);
|
||||||
|
@ -739,44 +865,18 @@ namespace ams::sm::impl {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dmnt record extensions. */
|
|
||||||
Result GetServiceRecord(ServiceRecord *out, ServiceName service) {
|
|
||||||
/* Validate service name. */
|
|
||||||
R_TRY(ValidateServiceName(service));
|
|
||||||
|
|
||||||
/* Validate that the service exists. */
|
|
||||||
const ServiceInfo *service_info = GetServiceInfo(service);
|
|
||||||
R_UNLESS(service_info != nullptr, sm::ResultNotRegistered());
|
|
||||||
|
|
||||||
GetServiceInfoRecord(out, service_info);
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
Result ListServiceRecords(ServiceRecord *out, u64 *out_count, u64 offset, u64 max_count) {
|
|
||||||
u64 count = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ServiceCountMax && count < max_count; i++) {
|
|
||||||
const ServiceInfo *service_info = &g_service_list[i];
|
|
||||||
if (service_info->name != InvalidServiceName) {
|
|
||||||
if (offset == 0) {
|
|
||||||
GetServiceInfoRecord(out++, service_info);
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
offset--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*out_count = 0;
|
|
||||||
return ResultSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Deferral extension (works around FS bug). */
|
/* Deferral extension (works around FS bug). */
|
||||||
Result EndInitialDefers() {
|
Result EndInitialDefers() {
|
||||||
|
/* Acquire exclusive access to global state. */
|
||||||
|
std::scoped_lock lk(g_mutex);
|
||||||
|
|
||||||
|
/* Note that we have ended the initial deferral period. */
|
||||||
g_ended_initial_defers = true;
|
g_ended_initial_defers = true;
|
||||||
|
|
||||||
/* This might undefer some requests. */
|
/* This might undefer some requests. */
|
||||||
TriggerResume(InitiallyDeferredServiceName);
|
for (const auto &service_name : InitiallyDeferredServices) {
|
||||||
|
TriggerResume(service_name);
|
||||||
|
}
|
||||||
|
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,29 +23,25 @@ namespace ams::sm::impl {
|
||||||
void OnClientDisconnected(os::ProcessId process_id);
|
void OnClientDisconnected(os::ProcessId process_id);
|
||||||
|
|
||||||
/* Process management. */
|
/* Process management. */
|
||||||
Result RegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus, const void *acid_sac, size_t acid_sac_size, const void *aci_sac, size_t aci_sac_size);
|
Result RegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const void *acid_sac, size_t acid_sac_size, const void *aci_sac, size_t aci_sac_size);
|
||||||
Result UnregisterProcess(os::ProcessId process_id);
|
Result UnregisterProcess(os::ProcessId process_id);
|
||||||
|
|
||||||
/* Service management. */
|
/* Service management. */
|
||||||
Result HasService(bool *out, ServiceName service);
|
Result HasService(bool *out, ServiceName service);
|
||||||
Result WaitService(ServiceName service);
|
Result WaitService(ServiceName service);
|
||||||
Result GetServiceHandle(Handle *out, os::ProcessId process_id, ServiceName service);
|
Result GetServiceHandle(svc::Handle *out, os::ProcessId process_id, ServiceName service);
|
||||||
Result RegisterService(Handle *out, os::ProcessId process_id, ServiceName service, size_t max_sessions, bool is_light);
|
Result RegisterService(svc::Handle *out, os::ProcessId process_id, ServiceName service, size_t max_sessions, bool is_light);
|
||||||
Result RegisterServiceForSelf(Handle *out, ServiceName service, size_t max_sessions);
|
Result RegisterServiceForSelf(svc::Handle *out, ServiceName service, size_t max_sessions);
|
||||||
Result UnregisterService(os::ProcessId process_id, ServiceName service);
|
Result UnregisterService(os::ProcessId process_id, ServiceName service);
|
||||||
|
|
||||||
/* Mitm extensions. */
|
/* Mitm extensions. */
|
||||||
Result HasMitm(bool *out, ServiceName service);
|
Result HasMitm(bool *out, ServiceName service);
|
||||||
Result WaitMitm(ServiceName service);
|
Result WaitMitm(ServiceName service);
|
||||||
Result InstallMitm(Handle *out, Handle *out_query, os::ProcessId process_id, ServiceName service);
|
Result InstallMitm(svc::Handle *out, svc::Handle *out_query, os::ProcessId process_id, ServiceName service);
|
||||||
Result UninstallMitm(os::ProcessId process_id, ServiceName service);
|
Result UninstallMitm(os::ProcessId process_id, ServiceName service);
|
||||||
Result DeclareFutureMitm(os::ProcessId process_id, ServiceName service);
|
Result DeclareFutureMitm(os::ProcessId process_id, ServiceName service);
|
||||||
Result ClearFutureMitm(os::ProcessId process_id, ServiceName service);
|
Result ClearFutureMitm(os::ProcessId process_id, ServiceName service);
|
||||||
Result AcknowledgeMitmSession(MitmProcessInfo *out_info, Handle *out_hnd, os::ProcessId process_id, ServiceName service);
|
Result AcknowledgeMitmSession(MitmProcessInfo *out_info, svc::Handle *out_hnd, os::ProcessId process_id, ServiceName service);
|
||||||
|
|
||||||
/* Dmnt record extensions. */
|
|
||||||
Result GetServiceRecord(ServiceRecord *out, ServiceName service);
|
|
||||||
Result ListServiceRecords(ServiceRecord *out, u64 *out_count, u64 offset, u64 max_count);
|
|
||||||
|
|
||||||
/* Deferral extension (works around FS bug). */
|
/* Deferral extension (works around FS bug). */
|
||||||
Result EndInitialDefers();
|
Result EndInitialDefers();
|
||||||
|
|
|
@ -1,124 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "sm_wait_list.hpp"
|
|
||||||
|
|
||||||
namespace ams::sm::impl {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
static constexpr size_t DeferredSessionCountMax = 0x40;
|
|
||||||
|
|
||||||
struct WaitListEntry {
|
|
||||||
sm::ServiceName service;
|
|
||||||
os::WaitableHolderType *session;
|
|
||||||
};
|
|
||||||
|
|
||||||
constinit WaitListEntry g_entries[DeferredSessionCountMax];
|
|
||||||
constinit WaitListEntry *g_processing_entry = nullptr;
|
|
||||||
constinit ServiceName g_triggered_service = InvalidServiceName;
|
|
||||||
|
|
||||||
WaitListEntry *Find(ServiceName service) {
|
|
||||||
for (auto &entry : g_entries) {
|
|
||||||
if (entry.service == service) {
|
|
||||||
return std::addressof(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Result StartRegisterRetry(ServiceName service) {
|
|
||||||
/* Check that we're not already processing a retry. */
|
|
||||||
AMS_ABORT_UNLESS(g_processing_entry == nullptr);
|
|
||||||
|
|
||||||
/* Find a free entry. */
|
|
||||||
auto *entry = Find(InvalidServiceName);
|
|
||||||
R_UNLESS(entry != nullptr, sm::ResultOutOfProcesses());
|
|
||||||
|
|
||||||
/* Initialize the entry. */
|
|
||||||
entry->service = service;
|
|
||||||
entry->session = nullptr;
|
|
||||||
|
|
||||||
/* Set the processing entry. */
|
|
||||||
g_processing_entry = entry;
|
|
||||||
|
|
||||||
return sf::ResultRequestDeferredByUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessRegisterRetry(os::WaitableHolderType *session_holder) {
|
|
||||||
/* Verify that we have a processing entry. */
|
|
||||||
AMS_ABORT_UNLESS(g_processing_entry != nullptr);
|
|
||||||
|
|
||||||
/* Process the session. */
|
|
||||||
g_processing_entry->session = session_holder;
|
|
||||||
g_processing_entry = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TriggerResume(ServiceName service) {
|
|
||||||
/* Check that we haven't already triggered a resume. */
|
|
||||||
AMS_ABORT_UNLESS(g_triggered_service == InvalidServiceName);
|
|
||||||
|
|
||||||
/* Set the triggered resume. */
|
|
||||||
g_triggered_service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestAndResume(ResumeFunction resume_function) {
|
|
||||||
/* If we don't have a triggered service, there's nothing to do. */
|
|
||||||
if (g_triggered_service == InvalidServiceName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get and clear the triggered service. */
|
|
||||||
const auto resumed_service = g_triggered_service;
|
|
||||||
g_triggered_service = InvalidServiceName;
|
|
||||||
|
|
||||||
/* Process all entries. */
|
|
||||||
for (size_t i = 0; i < util::size(g_entries); /* ... */) {
|
|
||||||
auto &entry = g_entries[i];
|
|
||||||
if (entry.service == resumed_service) {
|
|
||||||
/* Get the entry's session. */
|
|
||||||
auto * const session = entry.session;
|
|
||||||
|
|
||||||
/* Clear the entry. */
|
|
||||||
entry.service = InvalidServiceName;
|
|
||||||
entry.session = nullptr;
|
|
||||||
|
|
||||||
/* Resume the request. */
|
|
||||||
R_TRY_CATCH(resume_function(session)) {
|
|
||||||
R_CATCH(sf::ResultRequestDeferred) {
|
|
||||||
ProcessRegisterRetry(session);
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
|
||||||
|
|
||||||
/* Handle nested resumes. */
|
|
||||||
if (g_triggered_service != InvalidServiceName) {
|
|
||||||
AMS_ABORT_UNLESS(g_triggered_service == resumed_service);
|
|
||||||
|
|
||||||
g_triggered_service = InvalidServiceName;
|
|
||||||
i = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advance. */
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms and conditions of the GNU General Public License,
|
|
||||||
* version 2, as published by the Free Software Foundation.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope 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/>.
|
|
||||||
*/
|
|
||||||
#include <stratosphere.hpp>
|
|
||||||
#include "sm_debug_monitor_service.hpp"
|
|
||||||
#include "impl/sm_service_manager.hpp"
|
|
||||||
|
|
||||||
namespace ams::sm {
|
|
||||||
|
|
||||||
Result DebugMonitorService::AtmosphereGetRecord(sf::Out<ServiceRecord> record, ServiceName service) {
|
|
||||||
return impl::GetServiceRecord(record.GetPointer(), service);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebugMonitorService::AtmosphereListRecords(const sf::OutArray<ServiceRecord> &records, sf::Out<u64> out_count, u64 offset) {
|
|
||||||
R_ABORT_UNLESS(impl::ListServiceRecords(records.GetPointer(), out_count.GetPointer(), offset, records.GetSize()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebugMonitorService::AtmosphereGetRecordSize(sf::Out<u64> record_size) {
|
|
||||||
record_size.SetValue(sizeof(ServiceRecord));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -14,11 +14,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "sm_user_service.hpp"
|
#include "sm_tipc_server.hpp"
|
||||||
#include "sm_manager_service.hpp"
|
|
||||||
#include "sm_debug_monitor_service.hpp"
|
|
||||||
#include "impl/sm_service_manager.hpp"
|
|
||||||
#include "impl/sm_wait_list.hpp"
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
extern u32 __start__;
|
extern u32 __start__;
|
||||||
|
@ -78,46 +74,6 @@ void __appExit(void) {
|
||||||
/* Nothing to clean up, because we're sm. */
|
/* Nothing to clean up, because we're sm. */
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
enum PortIndex {
|
|
||||||
PortIndex_User,
|
|
||||||
PortIndex_Manager,
|
|
||||||
PortIndex_DebugMonitor,
|
|
||||||
PortIndex_Count,
|
|
||||||
};
|
|
||||||
|
|
||||||
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count> {
|
|
||||||
private:
|
|
||||||
virtual ams::Result OnNeedsToAccept(int port_index, Server *server) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
using Allocator = sf::ExpHeapAllocator;
|
|
||||||
using ObjectFactory = sf::ObjectFactory<sf::ExpHeapAllocator::Policy>;
|
|
||||||
|
|
||||||
alignas(0x40) constinit u8 g_server_allocator_buffer[8_KB];
|
|
||||||
Allocator g_server_allocator;
|
|
||||||
|
|
||||||
ServerManager g_server_manager;
|
|
||||||
|
|
||||||
ams::Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
|
|
||||||
switch (port_index) {
|
|
||||||
case PortIndex_User:
|
|
||||||
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<sm::impl::IUserInterface, sm::UserService>(std::addressof(g_server_allocator)));
|
|
||||||
case PortIndex_Manager:
|
|
||||||
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<sm::impl::IManagerInterface, sm::ManagerService>(std::addressof(g_server_allocator)));
|
|
||||||
case PortIndex_DebugMonitor:
|
|
||||||
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<sm::impl::IDebugMonitorInterface, sm::DebugMonitorService>(std::addressof(g_server_allocator)));
|
|
||||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ams::Result ResumeImpl(os::WaitableHolderType *session_holder) {
|
|
||||||
return g_server_manager.Process(session_holder);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void *operator new(size_t size) {
|
void *operator new(size_t size) {
|
||||||
AMS_ABORT("operator new(size_t) was called");
|
AMS_ABORT("operator new(size_t) was called");
|
||||||
}
|
}
|
||||||
|
@ -144,52 +100,11 @@ int main(int argc, char **argv)
|
||||||
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(sm, Main));
|
os::SetThreadNamePointer(os::GetCurrentThread(), AMS_GET_SYSTEM_THREAD_NAME(sm, Main));
|
||||||
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(sm, Main));
|
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(sm, Main));
|
||||||
|
|
||||||
/* Setup server allocator. */
|
/* Initialize the server. */
|
||||||
g_server_allocator.Attach(lmem::CreateExpHeap(g_server_allocator_buffer, sizeof(g_server_allocator_buffer), lmem::CreateOption_None));
|
sm::InitializeTipcServer();
|
||||||
|
|
||||||
/* Create sm:, (and thus allow things to register to it). */
|
/* Loop forever, processing our services. */
|
||||||
{
|
sm::LoopProcessTipcServer();
|
||||||
Handle sm_h;
|
|
||||||
R_ABORT_UNLESS(svc::ManageNamedPort(&sm_h, "sm:", 0x40));
|
|
||||||
g_server_manager.RegisterServer(PortIndex_User, sm_h);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create sm:m manually. */
|
|
||||||
{
|
|
||||||
Handle smm_h;
|
|
||||||
R_ABORT_UNLESS(sm::impl::RegisterServiceForSelf(&smm_h, sm::ServiceName::Encode("sm:m"), 1));
|
|
||||||
g_server_manager.RegisterServer(PortIndex_Manager, smm_h);
|
|
||||||
sm::impl::TestAndResume(ResumeImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*===== ATMOSPHERE EXTENSION =====*/
|
|
||||||
/* Create sm:dmnt manually. */
|
|
||||||
{
|
|
||||||
Handle smdmnt_h;
|
|
||||||
R_ABORT_UNLESS(sm::impl::RegisterServiceForSelf(&smdmnt_h, sm::ServiceName::Encode("sm:dmnt"), 1));
|
|
||||||
g_server_manager.RegisterServer(PortIndex_DebugMonitor, smdmnt_h);
|
|
||||||
sm::impl::TestAndResume(ResumeImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*================================*/
|
|
||||||
|
|
||||||
/* Loop forever, servicing our services. */
|
|
||||||
while (true) {
|
|
||||||
/* Get the next signaled holder. */
|
|
||||||
auto *holder = g_server_manager.WaitSignaled();
|
|
||||||
AMS_ABORT_UNLESS(holder != nullptr);
|
|
||||||
|
|
||||||
/* Process the holder. */
|
|
||||||
R_TRY_CATCH(g_server_manager.Process(holder)) {
|
|
||||||
R_CATCH(sf::ResultRequestDeferred) {
|
|
||||||
sm::impl::ProcessRegisterRetry(holder);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
|
||||||
|
|
||||||
/* Test to see if anything can be undeferred. */
|
|
||||||
sm::impl::TestAndResume(ResumeImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This can never be reached. */
|
/* This can never be reached. */
|
||||||
AMS_ASSUME(false);
|
AMS_ASSUME(false);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
namespace ams::sm {
|
namespace ams::sm {
|
||||||
|
|
||||||
Result ManagerService::RegisterProcess(os::ProcessId process_id, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac) {
|
Result ManagerService::RegisterProcess(os::ProcessId process_id, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac) {
|
||||||
return impl::RegisterProcess(process_id, ncm::InvalidProgramId, cfg::OverrideStatus{}, acid_sac.GetPointer(), acid_sac.GetSize(), aci_sac.GetPointer(), aci_sac.GetSize());
|
return impl::RegisterProcess(process_id, ncm::InvalidProgramId, cfg::OverrideStatus{}, acid_sac.GetPointer(), acid_sac.GetSize(), aci_sac.GetPointer(), aci_sac.GetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,11 +31,11 @@ namespace ams::sm {
|
||||||
R_ABORT_UNLESS(impl::EndInitialDefers());
|
R_ABORT_UNLESS(impl::EndInitialDefers());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ManagerService::AtmosphereHasMitm(sf::Out<bool> out, ServiceName service) {
|
void ManagerService::AtmosphereHasMitm(tipc::Out<bool> out, ServiceName service) {
|
||||||
R_ABORT_UNLESS(impl::HasMitm(out.GetPointer(), service));
|
R_ABORT_UNLESS(impl::HasMitm(out.GetPointer(), service));
|
||||||
}
|
}
|
||||||
|
|
||||||
Result ManagerService::AtmosphereRegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac) {
|
Result ManagerService::AtmosphereRegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac) {
|
||||||
/* This takes in a program id and override status, unlike RegisterProcess. */
|
/* This takes in a program id and override status, unlike RegisterProcess. */
|
||||||
return impl::RegisterProcess(process_id, program_id, override_status, acid_sac.GetPointer(), acid_sac.GetSize(), aci_sac.GetPointer(), aci_sac.GetSize());
|
return impl::RegisterProcess(process_id, program_id, override_status, acid_sac.GetPointer(), acid_sac.GetSize(), aci_sac.GetPointer(), aci_sac.GetSize());
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,11 @@ namespace ams::sm {
|
||||||
/* Service definition. */
|
/* Service definition. */
|
||||||
class ManagerService {
|
class ManagerService {
|
||||||
public:
|
public:
|
||||||
Result RegisterProcess(os::ProcessId process_id, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac);
|
Result RegisterProcess(os::ProcessId process_id, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac);
|
||||||
Result UnregisterProcess(os::ProcessId process_id);
|
Result UnregisterProcess(os::ProcessId process_id);
|
||||||
void AtmosphereEndInitDefers();
|
void AtmosphereEndInitDefers();
|
||||||
void AtmosphereHasMitm(sf::Out<bool> out, ServiceName service);
|
void AtmosphereHasMitm(tipc::Out<bool> out, ServiceName service);
|
||||||
Result AtmosphereRegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const sf::InBuffer &acid_sac, const sf::InBuffer &aci_sac);
|
Result AtmosphereRegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus override_status, const tipc::InBuffer acid_sac, const tipc::InBuffer aci_sac);
|
||||||
};
|
};
|
||||||
static_assert(sm::impl::IsIManagerInterface<ManagerService>);
|
static_assert(sm::impl::IsIManagerInterface<ManagerService>);
|
||||||
|
|
||||||
|
|
180
stratosphere/sm/source/sm_tipc_server.cpp
Normal file
180
stratosphere/sm/source/sm_tipc_server.cpp
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "sm_tipc_server.hpp"
|
||||||
|
#include "sm_user_service.hpp"
|
||||||
|
#include "sm_manager_service.hpp"
|
||||||
|
#include "sm_wait_list.hpp"
|
||||||
|
#include "impl/sm_service_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::sm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* Server limit definitions. */
|
||||||
|
enum PortIndex : size_t {
|
||||||
|
PortIndex_Manager,
|
||||||
|
PortIndex_User,
|
||||||
|
PortIndex_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr inline size_t NumTipcPorts = static_cast<size_t>(PortIndex_Count);
|
||||||
|
constexpr inline size_t MaxSessionsManager = 1;
|
||||||
|
constexpr inline size_t MaxSessionsUser = 0x50 + 8 - MaxSessionsManager;
|
||||||
|
|
||||||
|
constexpr inline size_t MaxSessionsTotal = MaxSessionsManager + MaxSessionsUser;
|
||||||
|
|
||||||
|
|
||||||
|
static_assert(MaxSessionsTotal % NumTipcPorts == 0);
|
||||||
|
|
||||||
|
/* Define WaitList class. */
|
||||||
|
class WaitList {
|
||||||
|
public:
|
||||||
|
using Key = sm::ServiceName;
|
||||||
|
private:
|
||||||
|
struct Entry {
|
||||||
|
sm::ServiceName service_name{sm::InvalidServiceName};
|
||||||
|
tipc::WaitableObject object{};
|
||||||
|
u8 message_buffer[svc::ipc::MessageBufferSize];
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
Entry m_entries[MaxSessionsTotal / NumTipcPorts]{};
|
||||||
|
Entry *m_processing_entry{};
|
||||||
|
public:
|
||||||
|
constexpr WaitList() = default;
|
||||||
|
public:
|
||||||
|
Result StartRegisterRetry(sm::ServiceName service_name) {
|
||||||
|
/* Check that we're not already processing a retry. */
|
||||||
|
AMS_ABORT_UNLESS(m_processing_entry == nullptr);
|
||||||
|
|
||||||
|
/* Find a free entry. */
|
||||||
|
Entry *free_entry = nullptr;
|
||||||
|
for (auto &entry : m_entries) {
|
||||||
|
if (entry.service_name == InvalidServiceName) {
|
||||||
|
free_entry = std::addressof(entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify that we found a free entry. */
|
||||||
|
R_UNLESS(free_entry != nullptr, sm::ResultOutOfProcesses());
|
||||||
|
|
||||||
|
/* Populate the entry. */
|
||||||
|
free_entry->service_name = service_name;
|
||||||
|
std::memcpy(free_entry->message_buffer, svc::ipc::GetMessageBuffer(), util::size(free_entry->message_buffer));
|
||||||
|
|
||||||
|
/* Set the processing entry. */
|
||||||
|
m_processing_entry = free_entry;
|
||||||
|
|
||||||
|
/* Return the special request deferral result. */
|
||||||
|
return tipc::ResultRequestDeferred();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessRegisterRetry(tipc::WaitableObject &object) {
|
||||||
|
/* Verify that we have a processing entry. */
|
||||||
|
AMS_ABORT_UNLESS(m_processing_entry != nullptr);
|
||||||
|
|
||||||
|
/* Set the entry's object. */
|
||||||
|
m_processing_entry->object = object;
|
||||||
|
|
||||||
|
/* Clear our processing entry. */
|
||||||
|
m_processing_entry = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestResume(sm::ServiceName service_name) {
|
||||||
|
/* Check that we have a matching service name. */
|
||||||
|
for (const auto &entry : m_entries) {
|
||||||
|
if (entry.service_name == service_name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename PortManagerType>
|
||||||
|
void Resume(sm::ServiceName service_name, PortManagerType *port_manager) {
|
||||||
|
/* Resume (and clear) all matching entries. */
|
||||||
|
for (auto &entry : m_entries) {
|
||||||
|
if (entry.service_name == service_name) {
|
||||||
|
/* Copy the saved message buffer. */
|
||||||
|
std::memcpy(svc::ipc::GetMessageBuffer(), entry.message_buffer, svc::ipc::MessageBufferSize);
|
||||||
|
|
||||||
|
/* Resume the request. */
|
||||||
|
R_TRY_CATCH(port_manager->ProcessRequest(entry.object)) {
|
||||||
|
R_CATCH(tipc::ResultRequestDeferred) {
|
||||||
|
this->ProcessRegisterRetry(entry.object);
|
||||||
|
}
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
/* Clear the entry's service name. */
|
||||||
|
entry.service_name = sm::InvalidServiceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Define port metadata. */
|
||||||
|
using UserPortMeta = tipc::PortMeta<MaxSessionsUser, impl::IUserInterface, UserService, tipc::SlabAllocator>;
|
||||||
|
using ManagerPortMeta = tipc::PortMeta<MaxSessionsManager, impl::IManagerInterface, ManagerService, tipc::SingletonAllocator>;
|
||||||
|
|
||||||
|
/* Define server manager global. */
|
||||||
|
using ServerManager = tipc::ServerManagerWithDeferral<sm::WaitList, ManagerPortMeta, UserPortMeta>;
|
||||||
|
|
||||||
|
ServerManager g_server_manager;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeTipcServer() {
|
||||||
|
/* Initialize the server manager. */
|
||||||
|
g_server_manager.Initialize();
|
||||||
|
|
||||||
|
/* Create the handles for our ports. */
|
||||||
|
svc::Handle user_port_handle = svc::InvalidHandle, manager_port_handle = svc::InvalidHandle;
|
||||||
|
{
|
||||||
|
/* Create the user port handle. */
|
||||||
|
R_ABORT_UNLESS(svc::ManageNamedPort(std::addressof(user_port_handle), "sm:", MaxSessionsUser));
|
||||||
|
|
||||||
|
/* Create the manager port handle. */
|
||||||
|
R_ABORT_UNLESS(impl::RegisterServiceForSelf(std::addressof(manager_port_handle), sm::ServiceName::Encode("sm:m"), MaxSessionsManager));
|
||||||
|
|
||||||
|
/* TODO: Debug Monitor port? */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register the ports. */
|
||||||
|
g_server_manager.RegisterPort<PortIndex_Manager>(manager_port_handle);
|
||||||
|
g_server_manager.RegisterPort<PortIndex_User >(user_port_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoopProcessTipcServer() {
|
||||||
|
/* Loop processing the server on all threads. */
|
||||||
|
g_server_manager.LoopAuto();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result StartRegisterRetry(sm::ServiceName service_name) {
|
||||||
|
/* Get the port manager from where it's saved in TLS. */
|
||||||
|
auto *port_manager = reinterpret_cast<typename ServerManager::PortManagerBase *>(os::GetTlsValue(g_server_manager.GetTlsSlot()));
|
||||||
|
|
||||||
|
/* Register the retry. */
|
||||||
|
return port_manager->StartRegisterRetry(service_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerResume(sm::ServiceName service_name) {
|
||||||
|
/* Trigger a resumption. */
|
||||||
|
g_server_manager.TriggerResume(service_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,19 +13,12 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
namespace ams::sm {
|
namespace ams::sm {
|
||||||
|
|
||||||
/* Service definition. */
|
void InitializeTipcServer();
|
||||||
class DebugMonitorService {
|
void LoopProcessTipcServer();
|
||||||
public:
|
|
||||||
Result AtmosphereGetRecord(sf::Out<ServiceRecord> record, ServiceName service);
|
|
||||||
void AtmosphereListRecords(const sf::OutArray<ServiceRecord> &records, sf::Out<u64> out_count, u64 offset);
|
|
||||||
void AtmosphereGetRecordSize(sf::Out<u64> record_size);
|
|
||||||
};
|
|
||||||
static_assert(sm::impl::IsIDebugMonitorInterface<DebugMonitorService>);
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,58 +20,58 @@
|
||||||
namespace ams::sm {
|
namespace ams::sm {
|
||||||
|
|
||||||
UserService::~UserService() {
|
UserService::~UserService() {
|
||||||
if (this->has_initialized) {
|
if (m_initialized) {
|
||||||
impl::OnClientDisconnected(this->process_id);
|
impl::OnClientDisconnected(m_process_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::RegisterClient(const sf::ClientProcessId &client_process_id) {
|
Result UserService::RegisterClient(const tipc::ClientProcessId client_process_id) {
|
||||||
this->process_id = client_process_id.GetValue();
|
m_process_id = client_process_id.value;
|
||||||
this->has_initialized = true;
|
m_initialized = true;
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::EnsureInitialized() {
|
Result UserService::EnsureInitialized() {
|
||||||
R_UNLESS(this->has_initialized, sm::ResultInvalidClient());
|
R_UNLESS(m_initialized, sm::ResultInvalidClient());
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::GetServiceHandle(sf::OutMoveHandle out_h, ServiceName service) {
|
Result UserService::GetServiceHandle(tipc::OutMoveHandle out_h, ServiceName service) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::GetServiceHandle(out_h.GetHandlePointer(), this->process_id, service);
|
return impl::GetServiceHandle(out_h.GetHandlePointer(), m_process_id, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::RegisterService(sf::OutMoveHandle out_h, ServiceName service, u32 max_sessions, bool is_light) {
|
Result UserService::RegisterService(tipc::OutMoveHandle out_h, ServiceName service, u32 max_sessions, bool is_light) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::RegisterService(out_h.GetHandlePointer(), this->process_id, service, max_sessions, is_light);
|
return impl::RegisterService(out_h.GetHandlePointer(), m_process_id, service, max_sessions, is_light);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::UnregisterService(ServiceName service) {
|
Result UserService::UnregisterService(ServiceName service) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::UnregisterService(this->process_id, service);
|
return impl::UnregisterService(m_process_id, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::DetachClient(const sf::ClientProcessId &client_process_id) {
|
Result UserService::DetachClient(const tipc::ClientProcessId client_process_id) {
|
||||||
this->has_initialized = false;
|
m_initialized = false;
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::AtmosphereInstallMitm(sf::OutMoveHandle srv_h, sf::OutMoveHandle qry_h, ServiceName service) {
|
Result UserService::AtmosphereInstallMitm(tipc::OutMoveHandle srv_h, tipc::OutMoveHandle qry_h, ServiceName service) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::InstallMitm(srv_h.GetHandlePointer(), qry_h.GetHandlePointer(), this->process_id, service);
|
return impl::InstallMitm(srv_h.GetHandlePointer(), qry_h.GetHandlePointer(), m_process_id, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::AtmosphereUninstallMitm(ServiceName service) {
|
Result UserService::AtmosphereUninstallMitm(ServiceName service) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::UninstallMitm(this->process_id, service);
|
return impl::UninstallMitm(m_process_id, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::AtmosphereAcknowledgeMitmSession(sf::Out<MitmProcessInfo> client_info, sf::OutMoveHandle fwd_h, ServiceName service) {
|
Result UserService::AtmosphereAcknowledgeMitmSession(tipc::Out<MitmProcessInfo> client_info, tipc::OutMoveHandle fwd_h, ServiceName service) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::AcknowledgeMitmSession(client_info.GetPointer(), fwd_h.GetHandlePointer(), this->process_id, service);
|
return impl::AcknowledgeMitmSession(client_info.GetPointer(), fwd_h.GetHandlePointer(), m_process_id, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::AtmosphereHasMitm(sf::Out<bool> out, ServiceName service) {
|
Result UserService::AtmosphereHasMitm(tipc::Out<bool> out, ServiceName service) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::HasMitm(out.GetPointer(), service);
|
return impl::HasMitm(out.GetPointer(), service);
|
||||||
}
|
}
|
||||||
|
@ -83,16 +83,15 @@ namespace ams::sm {
|
||||||
|
|
||||||
Result UserService::AtmosphereDeclareFutureMitm(ServiceName service) {
|
Result UserService::AtmosphereDeclareFutureMitm(ServiceName service) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::DeclareFutureMitm(this->process_id, service);
|
return impl::DeclareFutureMitm(m_process_id, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result UserService::AtmosphereClearFutureMitm(ServiceName service) {
|
Result UserService::AtmosphereClearFutureMitm(ServiceName service) {
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::ClearFutureMitm(this->process_id, service);
|
return impl::ClearFutureMitm(m_process_id, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result UserService::AtmosphereHasService(tipc::Out<bool> out, ServiceName service) {
|
||||||
Result UserService::AtmosphereHasService(sf::Out<bool> out, ServiceName service) {
|
|
||||||
R_TRY(this->EnsureInitialized());
|
R_TRY(this->EnsureInitialized());
|
||||||
return impl::HasService(out.GetPointer(), service);
|
return impl::HasService(out.GetPointer(), service);
|
||||||
}
|
}
|
||||||
|
@ -103,3 +102,6 @@ namespace ams::sm {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Include the backwards compatibility shim for CMIF. */
|
||||||
|
#include "sm_user_service_cmif_shim.inc"
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
@ -22,32 +21,37 @@ namespace ams::sm {
|
||||||
/* Service definition. */
|
/* Service definition. */
|
||||||
class UserService {
|
class UserService {
|
||||||
private:
|
private:
|
||||||
os::ProcessId process_id = os::InvalidProcessId;
|
os::ProcessId m_process_id;
|
||||||
bool has_initialized = false;
|
bool m_initialized;
|
||||||
|
public:
|
||||||
|
constexpr UserService() : m_process_id{os::InvalidProcessId}, m_initialized{false} { /* ... */ }
|
||||||
|
virtual ~UserService();
|
||||||
private:
|
private:
|
||||||
Result EnsureInitialized();
|
Result EnsureInitialized();
|
||||||
public:
|
|
||||||
virtual ~UserService();
|
|
||||||
public:
|
public:
|
||||||
/* Official commands. */
|
/* Official commands. */
|
||||||
Result RegisterClient(const sf::ClientProcessId &client_process_id);
|
Result RegisterClient(const tipc::ClientProcessId client_process_id);
|
||||||
Result GetServiceHandle(sf::OutMoveHandle out_h, ServiceName service);
|
Result GetServiceHandle(tipc::OutMoveHandle out_h, ServiceName service);
|
||||||
Result RegisterService(sf::OutMoveHandle out_h, ServiceName service, u32 max_sessions, bool is_light);
|
Result RegisterService(tipc::OutMoveHandle out_h, ServiceName service, u32 max_sessions, bool is_light);
|
||||||
Result UnregisterService(ServiceName service);
|
Result UnregisterService(ServiceName service);
|
||||||
Result DetachClient(const sf::ClientProcessId &client_process_id);
|
Result DetachClient(const tipc::ClientProcessId client_process_id);
|
||||||
|
|
||||||
/* Atmosphere commands. */
|
/* Atmosphere commands. */
|
||||||
Result AtmosphereInstallMitm(sf::OutMoveHandle srv_h, sf::OutMoveHandle qry_h, ServiceName service);
|
Result AtmosphereInstallMitm(tipc::OutMoveHandle srv_h, tipc::OutMoveHandle qry_h, ServiceName service);
|
||||||
Result AtmosphereUninstallMitm(ServiceName service);
|
Result AtmosphereUninstallMitm(ServiceName service);
|
||||||
Result AtmosphereAcknowledgeMitmSession(sf::Out<MitmProcessInfo> client_info, sf::OutMoveHandle fwd_h, ServiceName service);
|
Result AtmosphereAcknowledgeMitmSession(tipc::Out<MitmProcessInfo> client_info, tipc::OutMoveHandle fwd_h, ServiceName service);
|
||||||
Result AtmosphereHasMitm(sf::Out<bool> out, ServiceName service);
|
Result AtmosphereHasMitm(tipc::Out<bool> out, ServiceName service);
|
||||||
Result AtmosphereWaitMitm(ServiceName service);
|
Result AtmosphereWaitMitm(ServiceName service);
|
||||||
Result AtmosphereDeclareFutureMitm(ServiceName service);
|
Result AtmosphereDeclareFutureMitm(ServiceName service);
|
||||||
Result AtmosphereClearFutureMitm(ServiceName service);
|
Result AtmosphereClearFutureMitm(ServiceName service);
|
||||||
|
|
||||||
Result AtmosphereHasService(sf::Out<bool> out, ServiceName service);
|
Result AtmosphereHasService(tipc::Out<bool> out, ServiceName service);
|
||||||
Result AtmosphereWaitService(ServiceName service);
|
Result AtmosphereWaitService(ServiceName service);
|
||||||
|
public:
|
||||||
|
/* Backwards compatibility layer for cmif. */
|
||||||
|
Result ProcessDefaultServiceCommand(const svc::ipc::MessageBuffer &message_buffer);
|
||||||
};
|
};
|
||||||
static_assert(sm::impl::IsIUserInterface<UserService>);
|
static_assert(sm::impl::IsIUserInterface<UserService>);
|
||||||
|
/* TODO: static assert that this is a tipc interface with default prototyping. */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
213
stratosphere/sm/source/sm_user_service_cmif_shim.inc
Normal file
213
stratosphere/sm/source/sm_user_service_cmif_shim.inc
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace ams::sm {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const u8 CmifResponseToQueryPointerBufferSize[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x53, 0x46, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(CmifCommandType_Request == 4);
|
||||||
|
|
||||||
|
constexpr const u8 CmifExpectedRequestHeaderForRegisterClientAndDetachClient[] = {
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const u8 CmifResponseToRegisterClientAndDetachClient[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x53, 0x46, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const u8 CmifExpectedRequestHeaderForGetServiceHandleAndUnregisterService[] = {
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const u8 CmifResponseToGetServiceHandleAndRegisterService[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x53, 0x46, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const u8 CmifResponseToUnregisterService[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x53, 0x46, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr const u8 CmifExpectedRequestHeaderForRegisterService[] = {
|
||||||
|
0x04, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(tipc::ResultRequestDeferred().GetValue() == 0xC823);
|
||||||
|
constexpr const u8 CmifResponseToForceProcessorDeferral[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0xC8, 0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UserService::ProcessDefaultServiceCommand(const svc::ipc::MessageBuffer &message_buffer) {
|
||||||
|
/* Parse the hipc headers. */
|
||||||
|
const svc::ipc::MessageBuffer::MessageHeader message_header(message_buffer);
|
||||||
|
const svc::ipc::MessageBuffer::SpecialHeader special_header(message_buffer, message_header);
|
||||||
|
|
||||||
|
/* Get the request tag. */
|
||||||
|
const auto tag = message_header.GetTag();
|
||||||
|
|
||||||
|
/* Handle the case where we received a control request. */
|
||||||
|
if (tag == CmifCommandType_Control || tag == CmifCommandType_ControlWithContext) {
|
||||||
|
/* The only control/control with context command which we support is QueryPointerBufferSize. */
|
||||||
|
/* We do not have a pointer buffer, and so our pointer buffer size is zero. */
|
||||||
|
/* Return the relevant hardcoded response. */
|
||||||
|
std::memcpy(message_buffer.GetBufferForDebug(), CmifResponseToQueryPointerBufferSize, sizeof(CmifResponseToQueryPointerBufferSize));
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We only support request (with context), from this point forwards. */
|
||||||
|
R_UNLESS((tag == CmifCommandType_Request || tag == CmifCommandType_RequestWithContext), tipc::ResultInvalidMethod());
|
||||||
|
|
||||||
|
/* For ease of parsing, we will alter the tag to be Request when it is RequestWithContext. */
|
||||||
|
if (tag == CmifCommandType_RequestWithContext) {
|
||||||
|
message_buffer.Set(svc::ipc::MessageBuffer::MessageHeader(CmifCommandType_Request,
|
||||||
|
message_header.GetHasSpecialHeader(),
|
||||||
|
message_header.GetPointerCount(),
|
||||||
|
message_header.GetSendCount(),
|
||||||
|
message_header.GetReceiveCount(),
|
||||||
|
message_header.GetExchangeCount(),
|
||||||
|
message_header.GetRawCount(),
|
||||||
|
message_header.GetReceiveListCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NOTE: Nintendo only supports RegisterClient and GetServiceHandle. However, it would break */
|
||||||
|
/* a substantial amount of homebrew system modules, if we were to not provide shims for some */
|
||||||
|
/* other commands (RegisterService, and UnregisterService, in particular). As such, we will */
|
||||||
|
/* do the nice thing, and accommodate this by providing backwards compatibility shims for */
|
||||||
|
/* those commands. */
|
||||||
|
|
||||||
|
/* Please note, though, that the atmosphere extensions are not shimmed, as performing all the required */
|
||||||
|
/* parsing for that seems unreasonable. Also, if you are using atmosphere extensions, you are probably */
|
||||||
|
/* used to my breaking shit regularly anyway, and I don't expect much of a fuss to be raised by you. */
|
||||||
|
/* I invite you to demonstrate to me that this expectation does not reflect reality. */
|
||||||
|
const u8 * const raw_message_buffer = static_cast<const u8 *>(message_buffer.GetBufferForDebug());
|
||||||
|
u8 * const out_message_buffer = static_cast<u8 *>(message_buffer.GetBufferForDebug());
|
||||||
|
|
||||||
|
if (std::memcmp(raw_message_buffer, CmifExpectedRequestHeaderForRegisterClientAndDetachClient, sizeof(CmifExpectedRequestHeaderForRegisterClientAndDetachClient)) == 0) {
|
||||||
|
/* Get the command id. */
|
||||||
|
const u32 command_id = *reinterpret_cast<const u32 *>(raw_message_buffer + 0x28);
|
||||||
|
|
||||||
|
/* Get the client process id. */
|
||||||
|
u64 client_process_id;
|
||||||
|
std::memcpy(std::addressof(client_process_id), raw_message_buffer + 0xC, sizeof(client_process_id));
|
||||||
|
|
||||||
|
if (command_id == 0) {
|
||||||
|
/* Invoke the handler for RegisterClient. */
|
||||||
|
R_ABORT_UNLESS(this->RegisterClient(tipc::ClientProcessId{ client_process_id }));
|
||||||
|
} else if (command_id == 4) {
|
||||||
|
/* Invoke the handler for DetachClient. */
|
||||||
|
R_ABORT_UNLESS(this->DetachClient(tipc::ClientProcessId{ client_process_id }));
|
||||||
|
} else {
|
||||||
|
return tipc::ResultInvalidMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Serialize output. */
|
||||||
|
std::memcpy(out_message_buffer, CmifResponseToRegisterClientAndDetachClient, sizeof(CmifResponseToRegisterClientAndDetachClient));
|
||||||
|
} else if (std::memcmp(raw_message_buffer, CmifExpectedRequestHeaderForGetServiceHandleAndUnregisterService, sizeof(CmifExpectedRequestHeaderForGetServiceHandleAndUnregisterService)) == 0) {
|
||||||
|
/* Get the command id. */
|
||||||
|
const u32 command_id = *reinterpret_cast<const u32 *>(raw_message_buffer + 0x18);
|
||||||
|
|
||||||
|
/* Get the service_name. */
|
||||||
|
sm::ServiceName service_name;
|
||||||
|
std::memcpy(std::addressof(service_name), raw_message_buffer + 0x20, sizeof(service_name));
|
||||||
|
|
||||||
|
if (command_id == 1) {
|
||||||
|
/* Invoke the handler for GetServiceHandle. */
|
||||||
|
tipc::MoveHandle out_handle;
|
||||||
|
const Result result = this->GetServiceHandle(std::addressof(out_handle), service_name);
|
||||||
|
|
||||||
|
/* Ensure that the output handle is invalid-handle, if we failed. */
|
||||||
|
if (R_FAILED(result)) {
|
||||||
|
out_handle = svc::InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Serialize output. */
|
||||||
|
if (R_SUCCEEDED(result) || !tipc::ResultRequestDeferred::Includes(result)) {
|
||||||
|
std::memcpy(out_message_buffer + 0x00, CmifResponseToGetServiceHandleAndRegisterService, sizeof(CmifResponseToGetServiceHandleAndRegisterService));
|
||||||
|
std::memcpy(out_message_buffer + 0x0C, std::addressof(out_handle), sizeof(out_handle));
|
||||||
|
std::memcpy(out_message_buffer + 0x18, std::addressof(result), sizeof(result));
|
||||||
|
} else {
|
||||||
|
std::memcpy(out_message_buffer, CmifResponseToForceProcessorDeferral, sizeof(CmifResponseToForceProcessorDeferral));
|
||||||
|
}
|
||||||
|
} else if (command_id == 3) {
|
||||||
|
/* Invoke the handler for UnregisterService. */
|
||||||
|
const Result result = this->UnregisterService(service_name);
|
||||||
|
|
||||||
|
/* Serialize output. */
|
||||||
|
if (R_SUCCEEDED(result) || !tipc::ResultRequestDeferred::Includes(result)) {
|
||||||
|
std::memcpy(out_message_buffer + 0x00, CmifResponseToUnregisterService, sizeof(CmifResponseToUnregisterService));
|
||||||
|
std::memcpy(out_message_buffer + 0x18, std::addressof(result), sizeof(result));
|
||||||
|
} else {
|
||||||
|
std::memcpy(out_message_buffer, CmifResponseToForceProcessorDeferral, sizeof(CmifResponseToForceProcessorDeferral));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return tipc::ResultInvalidMethod();
|
||||||
|
}
|
||||||
|
} else if (std::memcmp(raw_message_buffer, CmifExpectedRequestHeaderForRegisterService, sizeof(CmifExpectedRequestHeaderForRegisterService)) == 0) {
|
||||||
|
/* Get the command id. */
|
||||||
|
const u32 command_id = *reinterpret_cast<const u32 *>(raw_message_buffer + 0x18);
|
||||||
|
|
||||||
|
/* Get the service_name. */
|
||||||
|
sm::ServiceName service_name;
|
||||||
|
std::memcpy(std::addressof(service_name), raw_message_buffer + 0x20, sizeof(service_name));
|
||||||
|
|
||||||
|
/* Get "is light". */
|
||||||
|
bool is_light;
|
||||||
|
is_light = (raw_message_buffer[0x28] & 0x01) != 0;
|
||||||
|
|
||||||
|
/* Get the max sessions. */
|
||||||
|
u32 max_sessions;
|
||||||
|
std::memcpy(std::addressof(max_sessions), raw_message_buffer + 0x2C, sizeof(max_sessions));
|
||||||
|
|
||||||
|
if (command_id == 4) {
|
||||||
|
/* Invoke the handler for RegisterService. */
|
||||||
|
tipc::MoveHandle out_handle;
|
||||||
|
const Result result = this->RegisterService(std::addressof(out_handle), service_name, max_sessions, is_light);
|
||||||
|
|
||||||
|
/* Ensure that the output handle is invalid-handle, if we failed. */
|
||||||
|
if (R_FAILED(result)) {
|
||||||
|
out_handle = svc::InvalidHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Serialize output. */
|
||||||
|
if (R_SUCCEEDED(result) || !tipc::ResultRequestDeferred::Includes(result)) {
|
||||||
|
std::memcpy(out_message_buffer + 0x00, CmifResponseToGetServiceHandleAndRegisterService, sizeof(CmifResponseToGetServiceHandleAndRegisterService));
|
||||||
|
std::memcpy(out_message_buffer + 0x0C, std::addressof(out_handle), sizeof(out_handle));
|
||||||
|
std::memcpy(out_message_buffer + 0x18, std::addressof(result), sizeof(result));
|
||||||
|
} else {
|
||||||
|
std::memcpy(out_message_buffer, CmifResponseToForceProcessorDeferral, sizeof(CmifResponseToForceProcessorDeferral));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return tipc::ResultInvalidMethod();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return tipc::ResultInvalidMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,14 +16,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
namespace ams::sm::impl {
|
namespace ams::sm {
|
||||||
|
|
||||||
using ResumeFunction = Result (*)(os::WaitableHolderType *session_holder);
|
Result StartRegisterRetry(sm::ServiceName service_name);
|
||||||
|
|
||||||
Result StartRegisterRetry(ServiceName service);
|
void TriggerResume(sm::ServiceName service_name);
|
||||||
void ProcessRegisterRetry(os::WaitableHolderType *session_holder);
|
|
||||||
|
|
||||||
void TriggerResume(ServiceName service);
|
|
||||||
void TestAndResume(ResumeFunction resume_function);
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue