1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-22 11:56:40 +00:00

sm: reimplement using tipc instead of cmif (probably broken, untested)

This commit is contained in:
Michael Scire 2021-04-10 01:58:26 -07:00 committed by SciresM
parent 58776f5ba8
commit 57c8bc432d
27 changed files with 904 additions and 735 deletions

View file

@ -21,7 +21,7 @@ PRECOMPILED_HEADERS := $(CURRENT_DIRECTORY)/include/stratosphere.hpp
#PRECOMPILED_HEADERS :=
DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2
SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -flto
CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE)
CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS)
ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS)

View file

@ -25,4 +25,3 @@
#include <stratosphere/sm/impl/sm_user_interface.hpp>
#include <stratosphere/sm/impl/sm_manager_interface.hpp>
#include <stratosphere/sm/impl/sm_debug_monitor_interface.hpp>

View file

@ -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)

View file

@ -17,13 +17,13 @@
#pragma once
#include <vapours.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) \
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_SF_METHOD_INFO(C, H, 1, Result, UnregisterProcess, (os::ProcessId process_id), (process_id)) \
AMS_SF_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_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))
#define AMS_SM_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_METHOD_INFO(C, H, 65000, void, AtmosphereEndInitDefers, (), ()) \
AMS_TIPC_METHOD_INFO(C, H, 65001, void, AtmosphereHasMitm, (tipc::Out<bool> out, sm::ServiceName service), (out, service)) \
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)

View file

@ -17,22 +17,22 @@
#pragma once
#include <vapours.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) \
AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterClient, (const sf::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_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_SF_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_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_SF_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_SF_METHOD_INFO(C, H, 65004, Result, AtmosphereHasMitm, (sf::Out<bool> out, sm::ServiceName service), (out, service)) \
AMS_SF_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_SF_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_SF_METHOD_INFO(C, H, 65101, Result, AtmosphereWaitService, (sm::ServiceName service), (service))
#define AMS_SM_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_METHOD_INFO(C, H, 4, Result, DetachClient, (const tipc::ClientProcessId client_process_id), (client_process_id)) \
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_TIPC_METHOD_INFO(C, H, 65001, Result, AtmosphereUninstallMitm, (sm::ServiceName service), (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_TIPC_METHOD_INFO(C, H, 65004, Result, AtmosphereHasMitm, (tipc::Out<bool> out, sm::ServiceName service), (out, service)) \
AMS_TIPC_METHOD_INFO(C, H, 65005, Result, AtmosphereWaitMitm, (sm::ServiceName service), (service)) \
AMS_TIPC_METHOD_INFO(C, H, 65006, Result, AtmosphereDeclareFutureMitm, (sm::ServiceName service), (service)) \
AMS_TIPC_METHOD_INFO(C, H, 65007, Result, AtmosphereClearFutureMitm, (sm::ServiceName service), (service)) \
AMS_TIPC_METHOD_INFO(C, H, 65100, Result, AtmosphereHasService, (tipc::Out<bool> out, sm::ServiceName service), (out, 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)

View file

@ -55,18 +55,6 @@ namespace ams::sm {
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. */
struct MitmProcessInfo {
os::ProcessId process_id;

View file

@ -280,7 +280,7 @@ namespace ams::tipc::impl {
static constexpr size_t OutDataSize = util::AlignUp(OutDataOffsets[NumOutDatas], alignof(u32));
/* Useful because reasons. */
static constexpr size_t OutDataAlign = [] {
static constexpr size_t OutDataAlign = []() -> size_t {
if constexpr (std::tuple_size<OutDatas>::value) {
return alignof(typename std::tuple_element<0, OutDatas>::type);
}

View file

@ -132,6 +132,8 @@ namespace ams::tipc {
public:
constexpr ALWAYS_INLINE InArrayImpl() : BaseType() { /* ... */ }
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 const T *GetPointer() const {
@ -163,6 +165,8 @@ namespace ams::tipc {
public:
constexpr ALWAYS_INLINE OutArrayImpl() : BaseType() { /* ... */ }
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 T *GetPointer() const {

View file

@ -57,7 +57,7 @@ namespace ams::tipc {
public:
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. */
m_waitable_manager = manager;
@ -179,7 +179,7 @@ namespace ams::tipc {
constexpr ObjectManager() = default;
void Initialize(os::WaitableManagerType *manager) {
this->Initialize(manager, m_entries_storage, MaxObjects);
this->InitializeImpl(manager, m_entries_storage, MaxObjects);
}
};

View file

@ -237,13 +237,15 @@ namespace ams::tipc {
--m_num_sessions;
}
void StartRegisterRetry(ResumeKey key) {
Result StartRegisterRetry(ResumeKey key) {
if constexpr (IsDeferralSupported) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* 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));
/* 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);
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:
template<size_t Ix> requires (Ix < NumPorts)
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>

View file

@ -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();
}
}

View file

@ -14,8 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "impl/os_waitable_object_list.hpp"
#include "impl/os_timeout_helper.hpp"
#include "impl/os_waitable_object_list.hpp"
#include "impl/os_waitable_holder_impl.hpp"
namespace ams::os {
@ -399,4 +400,20 @@ namespace ams::os {
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;
}
}

View file

@ -19,7 +19,8 @@
namespace ams::settings::impl {
Result GetProductModel(s32 *out) {
return ::setsysGetProductModel(out);
static_assert(sizeof(*out) == sizeof(::SetSysProductModel));
return ::setsysGetProductModel(reinterpret_cast<::SetSysProductModel *>(out));
}
}

View file

@ -386,6 +386,10 @@ namespace ams::svc::ipc {
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 ALWAYS_INLINE void *GetBufferForDebug() const {
return this->buffer;
}
constexpr ALWAYS_INLINE size_t GetBufferSize() const {
return this->size;
}

View file

@ -15,19 +15,21 @@
*/
#include <stratosphere.hpp>
#include "sm_service_manager.hpp"
#include "sm_wait_list.hpp"
#include "../sm_wait_list.hpp"
namespace ams::sm::impl {
namespace {
/* 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 FutureMitmCountMax = 0x20;
static constexpr size_t AccessControlSizeMax = 0x200;
constexpr auto InitiallyDeferredServiceName = ServiceName::Encode("fsp-srv");
constexpr sm::ServiceName InitiallyDeferredServices[] = {
ServiceName::Encode("fsp-srv")
};
/* Types. */
struct ProcessInfo {
@ -36,86 +38,55 @@ namespace ams::sm::impl {
cfg::OverrideStatus override_status;
size_t access_control_size;
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. */
void GetMitmProcessInfo(MitmProcessInfo *out, os::ProcessId process_id);
constexpr ProcessInfo InvalidProcessInfo = {
.process_id = os::InvalidProcessId,
.program_id = ncm::InvalidProgramId,
.override_status = {},
.access_control_size = 0,
.access_control = {},
};
struct ServiceInfo {
ServiceName name;
os::ProcessId owner_process_id;
os::ProcessId mitm_process_id;
os::ProcessId mitm_waiting_ack_process_id;
os::ManagedHandle mitm_port_h;
os::ManagedHandle mitm_query_h;
os::ManagedHandle port_h;
os::ManagedHandle mitm_fwd_sess_h;
svc::Handle mitm_port_h;
svc::Handle mitm_query_h;
svc::Handle port_h;
svc::Handle mitm_fwd_sess_h;
s32 max_sessions;
bool is_light;
bool mitm_waiting_ack;
};
ServiceInfo() {
this->Free();
}
void Free() {
/* Close any open handles. */
this->port_h.Clear();
this->mitm_port_h.Clear();
this->mitm_query_h.Clear();
this->mitm_fwd_sess_h.Clear();
/* Reset all other members. */
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;
}
constexpr ServiceInfo InvalidServiceInfo = {
.name = sm::InvalidServiceName,
.owner_process_id = os::InvalidProcessId,
.mitm_process_id = os::InvalidProcessId,
.mitm_waiting_ack_process_id = os::InvalidProcessId,
.mitm_port_h = svc::InvalidHandle,
.mitm_query_h = svc::InvalidHandle,
.port_h = svc::InvalidHandle,
.mitm_fwd_sess_h = svc::InvalidHandle,
.max_sessions = 0,
.is_light = false,
.mitm_waiting_ack = false,
};
class AccessControlEntry {
private:
const u8 *entry;
size_t capacity;
const u8 *m_entry;
size_t m_capacity;
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 {
return AccessControlEntry(this->entry + this->GetSize(), this->capacity - this->GetSize());
return AccessControlEntry(m_entry + this->GetSize(), m_capacity - this->GetSize());
}
size_t GetSize() const {
@ -123,29 +94,29 @@ namespace ams::sm::impl {
}
size_t GetServiceNameSize() const {
return (this->entry[0] & 7) + 1;
return (m_entry[0] & 7) + 1;
}
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 {
return (this->entry[0] & 0x80) != 0;
return (m_entry[0] & 0x80) != 0;
}
bool IsWildcard() const {
return this->entry[this->GetServiceNameSize()] == '*';
return m_entry[this->GetServiceNameSize()] == '*';
}
bool IsValid() const {
/* Validate that we can access data. */
if (this->entry == nullptr || this->capacity == 0) {
if (m_entry == nullptr || m_capacity == 0) {
return false;
}
/* 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. */
ProcessInfo g_process_list[ProcessCountMax];
ServiceInfo g_service_list[ServiceCountMax];
ServiceName g_future_mitm_list[FutureMitmCountMax];
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];
}
/* NOTE: In 12.0.0, Nintendo added multithreaded processing to sm; however, official sm does not do */
/* 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 nullptr;
}
ProcessInfo *GetFreeProcessInfo() {
return GetProcessInfo(os::InvalidProcessId);
}
return list;
}();
bool HasProcessInfo(os::ProcessId process_id) {
return GetProcessInfo(process_id) != nullptr;
}
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;
/* Helper functionality. */
bool IsInitialProcess(os::ProcessId process_id) {
return g_initial_process_id_limits.IsInitialProcess(process_id);
}
@ -201,91 +193,6 @@ namespace ams::sm::impl {
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) {
/* Iterate over all entries in the access control, checking to see if we have a match. */
while (access_control.IsValid()) {
@ -298,7 +205,7 @@ namespace ams::sm::impl {
} else if (access_control.IsWildcard()) {
/* Also allow fuzzy match for wildcard. */
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);
@ -347,44 +254,161 @@ namespace ams::sm::impl {
/* 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. */
return service == InitiallyDeferredServiceName;
for (const auto &service_name : InitiallyDeferredServices) {
if (service == service_name) {
return true;
}
}
return false;
}
Result GetMitmServiceHandleImpl(Handle *out, ServiceInfo *service_info, const MitmProcessInfo &client_info) {
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. */
bool should_mitm;
{
Service srv { .session = service_info->mitm_query_h.Get() };
R_TRY(serviceDispatchInOut(&srv, 65000, client_info, should_mitm));
/* TODO: Convert mitm internal messaging to use tipc? */
::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. */
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. */
{
os::ManagedHandle fwd_hnd, hnd;
R_TRY(svcConnectToPort(fwd_hnd.GetPointer(), service_info->port_h.Get()));
R_TRY(svcConnectToPort(hnd.GetPointer(), service_info->mitm_port_h.Get()));
service_info->mitm_fwd_sess_h = std::move(fwd_hnd);
*out = hnd.Move();
/* Get the forward handle. */
svc::Handle fwd_hnd;
R_TRY(svc::ConnectToPort(std::addressof(fwd_hnd), service_info->port_h));
/* 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 = true;
service_info->mitm_waiting_ack = true;
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. */
*out = INVALID_HANDLE;
*out = svc::InvalidHandle;
/* Check if we should return a mitm handle. */
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. */
MitmProcessInfo client_info;
GetMitmProcessInfo(&client_info, process_id);
GetMitmProcessInfo(std::addressof(client_info), process_id);
if (!IsMitmDisallowed(client_info.program_id)) {
/* We're mitm'd. Assert, because mitm service host dead is an error state. */
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. */
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. */
R_TRY(ValidateServiceName(service));
@ -408,16 +432,16 @@ namespace ams::sm::impl {
R_UNLESS(free_service != nullptr, sm::ResultOutOfServices());
/* Create the new service. */
*out = INVALID_HANDLE;
Handle server_hnd = INVALID_HANDLE;
R_TRY(svcCreatePort(out, std::addressof(server_hnd), max_sessions, is_light, free_service->name.name));
*out = svc::InvalidHandle;
svc::Handle server_hnd = svc::InvalidHandle;
R_TRY(svc::CreatePort(out, std::addressof(server_hnd), max_sessions, is_light, reinterpret_cast<uintptr_t>(free_service->name.name)));
/* Save info. */
free_service->name = service;
free_service->name = service;
free_service->owner_process_id = process_id;
free_service->max_sessions = max_sessions;
free_service->is_light = is_light;
*free_service->port_h.GetPointerAndClear() = server_hnd;
free_service->max_sessions = max_sessions;
free_service->is_light = is_light;
free_service->port_h = server_hnd;
/* This might undefer some requests. */
TriggerResume(service);
@ -425,25 +449,42 @@ namespace ams::sm::impl {
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. */
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. */
if (process_id == os::InvalidProcessId) {
return;
}
/* Unregister all services a client hosts, on attached-client-close. */
for (size_t i = 0; i < ServiceCountMax; i++) {
if (g_service_list[i].name != InvalidServiceName && g_service_list[i].owner_process_id == process_id) {
g_service_list[i].Free();
for (auto &service_info : g_service_list) {
if (service_info.name != InvalidServiceName && service_info.owner_process_id == process_id) {
UnregisterServiceImpl(std::addressof(service_info));
}
}
}
/* 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) {
/* Acquire exclusive access to global state. */
std::scoped_lock lk(g_mutex);
/* Check that access control will fit in the ServiceInfo. */
R_UNLESS(aci_sac_size <= AccessControlSizeMax, sm::ResultTooLargeAccessControl());
@ -456,25 +497,34 @@ namespace ams::sm::impl {
R_TRY(ValidateAccessControl(AccessControlEntry(acid_sac, acid_sac_size), AccessControlEntry(aci_sac, aci_sac_size)));
/* Save info. */
proc->process_id = process_id;
proc->program_id = program_id;
proc->override_status = override_status;
proc->process_id = process_id;
proc->program_id = program_id;
proc->override_status = override_status;
proc->access_control_size = aci_sac_size;
std::memcpy(proc->access_control, aci_sac, proc->access_control_size);
return ResultSuccess();
}
Result UnregisterProcess(os::ProcessId process_id) {
/* Acquire exclusive access to global state. */
std::scoped_lock lk(g_mutex);
/* Find the process. */
ProcessInfo *proc = GetProcessInfo(process_id);
R_UNLESS(proc != nullptr, sm::ResultInvalidClient());
proc->Free();
/* Free the process. */
*proc = InvalidProcessInfo;
return ResultSuccess();
}
/* Service management. */
Result HasService(bool *out, ServiceName service) {
/* Acquire exclusive access to global state. */
std::scoped_lock lk(g_mutex);
/* Validate service name. */
R_TRY(ValidateServiceName(service));
@ -483,16 +533,24 @@ namespace ams::sm::impl {
}
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;
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);
/* Otherwise, we want to wait until the service is registered. */
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. */
R_TRY(ValidateServiceName(service));
@ -525,7 +583,10 @@ namespace ams::sm::impl {
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. */
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));
}
/* Check that the service isn't already registered. */
R_UNLESS(!HasServiceInfo(service), sm::ResultAlreadyRegistered());
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);
}
Result UnregisterService(os::ProcessId process_id, ServiceName service) {
/* Acquire exclusive access to global state. */
std::scoped_lock lk(g_mutex);
/* Validate service name. */
R_TRY(ValidateServiceName(service));
@ -562,31 +631,44 @@ namespace ams::sm::impl {
R_UNLESS(service_info->owner_process_id == process_id, sm::ResultNotAllowed());
/* Unregister the service. */
service_info->Free();
UnregisterServiceImpl(service_info);
return ResultSuccess();
}
/* Mitm extensions. */
Result HasMitm(bool *out, ServiceName service) {
/* Acquire exclusive access to global state. */
std::scoped_lock lk(g_mutex);
/* Validate service name. */
R_TRY(ValidateServiceName(service));
const ServiceInfo *service_info = GetServiceInfo(service);
*out = service_info != nullptr && IsValidProcessId(service_info->mitm_process_id);
/* Get whether we have a mitm. */
*out = HasMitm(service);
return ResultSuccess();
}
Result WaitMitm(ServiceName service) {
bool has_mitm = false;
R_TRY(impl::HasMitm(&has_mitm, service));
/* Acquire exclusive access to global state. */
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);
/* Otherwise, we want to wait until the service is registered. */
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. */
R_TRY(ValidateServiceName(service));
@ -601,20 +683,18 @@ namespace ams::sm::impl {
ServiceInfo *service_info = GetServiceInfo(service);
/* If it doesn't exist, defer until it does. */
if (service_info == nullptr) {
return StartRegisterRetry(service);
}
R_UNLESS(service_info != nullptr, StartRegisterRetry(service));
/* Validate that the service isn't already being mitm'd. */
R_UNLESS(!IsValidProcessId(service_info->mitm_process_id), sm::ResultAlreadyRegistered());
/* Always clear output. */
*out = INVALID_HANDLE;
*out_query = INVALID_HANDLE;
*out = svc::InvalidHandle;
*out_query = svc::InvalidHandle;
/* If we don't have a future mitm declaration, add one. */
/* 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) {
R_TRY(AddFutureMitmDeclaration(service));
}
@ -623,16 +703,26 @@ namespace ams::sm::impl {
/* Create mitm handles. */
{
os::ManagedHandle hnd, port_hnd, qry_hnd, mitm_qry_hnd;
R_TRY(svcCreatePort(hnd.GetPointer(), port_hnd.GetPointer(), service_info->max_sessions, service_info->is_light, service_info->name.name));
R_TRY(svcCreateSession(qry_hnd.GetPointer(), mitm_qry_hnd.GetPointer(), 0, 0));
/* Get the port handles. */
svc::Handle hnd, port_hnd;
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. */
service_info->mitm_process_id = process_id;
service_info->mitm_port_h = std::move(port_hnd);
service_info->mitm_query_h = std::move(mitm_qry_hnd);
*out = hnd.Move();
*out_query = qry_hnd.Move();
service_info->mitm_port_h = port_hnd;
service_info->mitm_query_h = mitm_qry_hnd;
*out = hnd;
*out_query = qry_hnd;
/* This might undefer some requests. */
TriggerResume(service);
@ -643,6 +733,9 @@ namespace ams::sm::impl {
}
Result UninstallMitm(os::ProcessId process_id, ServiceName service) {
/* Acquire exclusive access to global state. */
std::scoped_lock lk(g_mutex);
/* Validate service name. */
R_TRY(ValidateServiceName(service));
@ -659,12 +752,26 @@ namespace ams::sm::impl {
/* Validate that the client process_id is the mitm process. */
R_UNLESS(service_info->mitm_process_id == process_id, sm::ResultNotAllowed());
/* Free Mitm session info. */
service_info->FreeMitm();
/* Uninstall the mitm. */
{
/* 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();
}
Result DeclareFutureMitm(os::ProcessId process_id, ServiceName service) {
/* Acquire exclusive access to global state. */
std::scoped_lock lk(g_mutex);
/* Validate service name. */
R_TRY(ValidateServiceName(service));
@ -681,10 +788,14 @@ namespace ams::sm::impl {
/* Try to forward declare it. */
R_TRY(AddFutureMitmDeclaration(service));
return ResultSuccess();
}
Result ClearFutureMitm(os::ProcessId process_id, ServiceName service) {
/* Acquire exclusive access to global state. */
std::scoped_lock lk(g_mutex);
/* Validate service name. */
R_TRY(ValidateServiceName(service));
@ -709,10 +820,14 @@ namespace ams::sm::impl {
/* Clear the forward declaration. */
ClearFutureMitmDeclaration(service);
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. */
R_TRY(ValidateServiceName(service));
@ -731,7 +846,18 @@ namespace ams::sm::impl {
R_UNLESS(service_info->mitm_waiting_ack, sm::ResultNotAllowed());
/* 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. */
TriggerResume(service);
@ -739,44 +865,18 @@ namespace ams::sm::impl {
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). */
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;
/* This might undefer some requests. */
TriggerResume(InitiallyDeferredServiceName);
for (const auto &service_name : InitiallyDeferredServices) {
TriggerResume(service_name);
}
return ResultSuccess();
}

View file

@ -23,29 +23,25 @@ namespace ams::sm::impl {
void OnClientDisconnected(os::ProcessId process_id);
/* 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);
/* Service management. */
Result HasService(bool *out, ServiceName service);
Result WaitService(ServiceName service);
Result GetServiceHandle(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 RegisterServiceForSelf(Handle *out, ServiceName service, size_t max_sessions);
Result GetServiceHandle(svc::Handle *out, os::ProcessId process_id, ServiceName service);
Result RegisterService(svc::Handle *out, os::ProcessId process_id, ServiceName service, size_t max_sessions, bool is_light);
Result RegisterServiceForSelf(svc::Handle *out, ServiceName service, size_t max_sessions);
Result UnregisterService(os::ProcessId process_id, ServiceName service);
/* Mitm extensions. */
Result HasMitm(bool *out, 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 DeclareFutureMitm(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);
/* Dmnt record extensions. */
Result GetServiceRecord(ServiceRecord *out, ServiceName service);
Result ListServiceRecords(ServiceRecord *out, u64 *out_count, u64 offset, u64 max_count);
Result AcknowledgeMitmSession(MitmProcessInfo *out_info, svc::Handle *out_hnd, os::ProcessId process_id, ServiceName service);
/* Deferral extension (works around FS bug). */
Result EndInitialDefers();

View file

@ -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;
}
}
}

View file

@ -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));
}
}

View file

@ -14,11 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "sm_user_service.hpp"
#include "sm_manager_service.hpp"
#include "sm_debug_monitor_service.hpp"
#include "impl/sm_service_manager.hpp"
#include "impl/sm_wait_list.hpp"
#include "sm_tipc_server.hpp"
extern "C" {
extern u32 __start__;
@ -78,46 +74,6 @@ void __appExit(void) {
/* 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) {
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));
AMS_ASSERT(os::GetThreadPriority(os::GetCurrentThread()) == AMS_GET_SYSTEM_THREAD_PRIORITY(sm, Main));
/* Setup server allocator. */
g_server_allocator.Attach(lmem::CreateExpHeap(g_server_allocator_buffer, sizeof(g_server_allocator_buffer), lmem::CreateOption_None));
/* Initialize the server. */
sm::InitializeTipcServer();
/* Create sm:, (and thus allow things to register to it). */
{
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);
}
/* Loop forever, processing our services. */
sm::LoopProcessTipcServer();
/* This can never be reached. */
AMS_ASSUME(false);

View file

@ -19,7 +19,7 @@
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());
}
@ -31,11 +31,11 @@ namespace ams::sm {
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));
}
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. */
return impl::RegisterProcess(process_id, program_id, override_status, acid_sac.GetPointer(), acid_sac.GetSize(), aci_sac.GetPointer(), aci_sac.GetSize());
}

View file

@ -21,11 +21,11 @@ namespace ams::sm {
/* Service definition. */
class ManagerService {
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);
void AtmosphereEndInitDefers();
void AtmosphereHasMitm(sf::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);
void AtmosphereHasMitm(tipc::Out<bool> out, ServiceName service);
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>);

View 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);
}
}

View file

@ -13,19 +13,12 @@
* 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 <stratosphere.hpp>
namespace ams::sm {
/* Service definition. */
class DebugMonitorService {
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>);
void InitializeTipcServer();
void LoopProcessTipcServer();
}

View file

@ -20,58 +20,58 @@
namespace ams::sm {
UserService::~UserService() {
if (this->has_initialized) {
impl::OnClientDisconnected(this->process_id);
if (m_initialized) {
impl::OnClientDisconnected(m_process_id);
}
}
Result UserService::RegisterClient(const sf::ClientProcessId &client_process_id) {
this->process_id = client_process_id.GetValue();
this->has_initialized = true;
Result UserService::RegisterClient(const tipc::ClientProcessId client_process_id) {
m_process_id = client_process_id.value;
m_initialized = true;
return ResultSuccess();
}
Result UserService::EnsureInitialized() {
R_UNLESS(this->has_initialized, sm::ResultInvalidClient());
R_UNLESS(m_initialized, sm::ResultInvalidClient());
return ResultSuccess();
}
Result UserService::GetServiceHandle(sf::OutMoveHandle out_h, ServiceName service) {
Result UserService::GetServiceHandle(tipc::OutMoveHandle out_h, ServiceName service) {
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());
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) {
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) {
this->has_initialized = false;
Result UserService::DetachClient(const tipc::ClientProcessId client_process_id) {
m_initialized = false;
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());
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) {
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());
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());
return impl::HasMitm(out.GetPointer(), service);
}
@ -83,16 +83,15 @@ namespace ams::sm {
Result UserService::AtmosphereDeclareFutureMitm(ServiceName service) {
R_TRY(this->EnsureInitialized());
return impl::DeclareFutureMitm(this->process_id, service);
return impl::DeclareFutureMitm(m_process_id, service);
}
Result UserService::AtmosphereClearFutureMitm(ServiceName service) {
R_TRY(this->EnsureInitialized());
return impl::ClearFutureMitm(this->process_id, service);
return impl::ClearFutureMitm(m_process_id, service);
}
Result UserService::AtmosphereHasService(sf::Out<bool> out, ServiceName service) {
Result UserService::AtmosphereHasService(tipc::Out<bool> out, ServiceName service) {
R_TRY(this->EnsureInitialized());
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"

View file

@ -13,7 +13,6 @@
* 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 <stratosphere.hpp>
@ -22,32 +21,37 @@ namespace ams::sm {
/* Service definition. */
class UserService {
private:
os::ProcessId process_id = os::InvalidProcessId;
bool has_initialized = false;
os::ProcessId m_process_id;
bool m_initialized;
public:
constexpr UserService() : m_process_id{os::InvalidProcessId}, m_initialized{false} { /* ... */ }
virtual ~UserService();
private:
Result EnsureInitialized();
public:
virtual ~UserService();
public:
/* Official commands. */
Result RegisterClient(const sf::ClientProcessId &client_process_id);
Result GetServiceHandle(sf::OutMoveHandle out_h, ServiceName service);
Result RegisterService(sf::OutMoveHandle out_h, ServiceName service, u32 max_sessions, bool is_light);
Result RegisterClient(const tipc::ClientProcessId client_process_id);
Result GetServiceHandle(tipc::OutMoveHandle out_h, ServiceName service);
Result RegisterService(tipc::OutMoveHandle out_h, ServiceName service, u32 max_sessions, bool is_light);
Result UnregisterService(ServiceName service);
Result DetachClient(const sf::ClientProcessId &client_process_id);
Result DetachClient(const tipc::ClientProcessId client_process_id);
/* 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 AtmosphereAcknowledgeMitmSession(sf::Out<MitmProcessInfo> client_info, sf::OutMoveHandle fwd_h, ServiceName service);
Result AtmosphereHasMitm(sf::Out<bool> out, ServiceName service);
Result AtmosphereAcknowledgeMitmSession(tipc::Out<MitmProcessInfo> client_info, tipc::OutMoveHandle fwd_h, ServiceName service);
Result AtmosphereHasMitm(tipc::Out<bool> out, ServiceName service);
Result AtmosphereWaitMitm(ServiceName service);
Result AtmosphereDeclareFutureMitm(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);
public:
/* Backwards compatibility layer for cmif. */
Result ProcessDefaultServiceCommand(const svc::ipc::MessageBuffer &message_buffer);
};
static_assert(sm::impl::IsIUserInterface<UserService>);
/* TODO: static assert that this is a tipc interface with default prototyping. */
}

View 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();
}
}

View file

@ -16,14 +16,10 @@
#pragma once
#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 ProcessRegisterRetry(os::WaitableHolderType *session_holder);
void TriggerResume(ServiceName service);
void TestAndResume(ResumeFunction resume_function);
void TriggerResume(sm::ServiceName service_name);
}