From 57c8bc432df91c561d42c375b5ef32ee77471e87 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sat, 10 Apr 2021 01:58:26 -0700 Subject: [PATCH] sm: reimplement using tipc instead of cmif (probably broken, untested) --- libraries/libstratosphere/Makefile | 2 +- .../include/stratosphere/sm.hpp | 1 - .../sm/impl/sm_debug_monitor_interface.hpp | 27 - .../sm/impl/sm_manager_interface.hpp | 16 +- .../sm/impl/sm_user_interface.hpp | 34 +- .../include/stratosphere/sm/sm_types.hpp | 12 - .../impl/tipc_impl_command_serialization.hpp | 2 +- .../stratosphere/tipc/tipc_buffers.hpp | 4 + .../stratosphere/tipc/tipc_object_manager.hpp | 4 +- .../stratosphere/tipc/tipc_server_manager.hpp | 29 +- .../source/_test/test_tipc_serializer.cpp | 81 --- .../source/os/os_message_queue.cpp | 19 +- .../impl/settings_product_model_impl.cpp | 3 +- .../vapours/svc/ipc/svc_message_buffer.hpp | 4 + .../sm/source/impl/sm_service_manager.cpp | 628 ++++++++++-------- .../sm/source/impl/sm_service_manager.hpp | 16 +- stratosphere/sm/source/impl/sm_wait_list.cpp | 124 ---- .../sm/source/sm_debug_monitor_service.cpp | 34 - stratosphere/sm/source/sm_main.cpp | 95 +-- stratosphere/sm/source/sm_manager_service.cpp | 6 +- stratosphere/sm/source/sm_manager_service.hpp | 6 +- stratosphere/sm/source/sm_tipc_server.cpp | 180 +++++ ...monitor_service.hpp => sm_tipc_server.hpp} | 11 +- stratosphere/sm/source/sm_user_service.cpp | 48 +- stratosphere/sm/source/sm_user_service.hpp | 30 +- .../sm/source/sm_user_service_cmif_shim.inc | 213 ++++++ .../sm/source/{impl => }/sm_wait_list.hpp | 10 +- 27 files changed, 904 insertions(+), 735 deletions(-) delete mode 100644 libraries/libstratosphere/include/stratosphere/sm/impl/sm_debug_monitor_interface.hpp delete mode 100644 libraries/libstratosphere/source/_test/test_tipc_serializer.cpp delete mode 100644 stratosphere/sm/source/impl/sm_wait_list.cpp delete mode 100644 stratosphere/sm/source/sm_debug_monitor_service.cpp create mode 100644 stratosphere/sm/source/sm_tipc_server.cpp rename stratosphere/sm/source/{sm_debug_monitor_service.hpp => sm_tipc_server.hpp} (61%) create mode 100644 stratosphere/sm/source/sm_user_service_cmif_shim.inc rename stratosphere/sm/source/{impl => }/sm_wait_list.hpp (67%) diff --git a/libraries/libstratosphere/Makefile b/libraries/libstratosphere/Makefile index 57acad73b..20e0c6c7e 100644 --- a/libraries/libstratosphere/Makefile +++ b/libraries/libstratosphere/Makefile @@ -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) diff --git a/libraries/libstratosphere/include/stratosphere/sm.hpp b/libraries/libstratosphere/include/stratosphere/sm.hpp index 98e3f83ac..9e93cf0d3 100644 --- a/libraries/libstratosphere/include/stratosphere/sm.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm.hpp @@ -25,4 +25,3 @@ #include #include -#include diff --git a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_debug_monitor_interface.hpp b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_debug_monitor_interface.hpp deleted file mode 100644 index 4afa5fdc5..000000000 --- a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_debug_monitor_interface.hpp +++ /dev/null @@ -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 . - */ - -#pragma once -#include -#include -#include - -#define AMS_SM_I_DEBUG_MONITOR_INTERFACE_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereGetRecord, (sf::Out record, sm::ServiceName service), (record, service)) \ - AMS_SF_METHOD_INFO(C, H, 65001, void, AtmosphereListRecords, (const sf::OutArray &records, sf::Out out_count, u64 offset), (records, out_count, offset)) \ - AMS_SF_METHOD_INFO(C, H, 65002, void, AtmosphereGetRecordSize, (sf::Out record_size), (record_size)) - -AMS_SF_DEFINE_INTERFACE(ams::sm::impl, IDebugMonitorInterface, AMS_SM_I_DEBUG_MONITOR_INTERFACE_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_manager_interface.hpp b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_manager_interface.hpp index 8c6d11a95..542bb7b69 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_manager_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_manager_interface.hpp @@ -17,13 +17,13 @@ #pragma once #include #include -#include +#include -#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 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 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) diff --git a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp index f833abe3a..1001fbe0d 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp @@ -17,22 +17,22 @@ #pragma once #include #include -#include +#include -#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 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 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 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 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 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 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) diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp index 3a9dc531d..e02253a6b 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/sm_types.hpp @@ -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; diff --git a/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp index 9ef0a57a8..d0ad2e73f 100644 --- a/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp +++ b/libraries/libstratosphere/include/stratosphere/tipc/impl/tipc_impl_command_serialization.hpp @@ -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::value) { return alignof(typename std::tuple_element<0, OutDatas>::type); } diff --git a/libraries/libstratosphere/include/stratosphere/tipc/tipc_buffers.hpp b/libraries/libstratosphere/include/stratosphere/tipc/tipc_buffers.hpp index b859138cb..cdc7b764e 100644 --- a/libraries/libstratosphere/include/stratosphere/tipc/tipc_buffers.hpp +++ b/libraries/libstratosphere/include/stratosphere/tipc/tipc_buffers.hpp @@ -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(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(ptr), num_elements * sizeof(T)) { /* ... */ } constexpr ALWAYS_INLINE T *GetPointer() const { diff --git a/libraries/libstratosphere/include/stratosphere/tipc/tipc_object_manager.hpp b/libraries/libstratosphere/include/stratosphere/tipc/tipc_object_manager.hpp index c14942e9f..91a60057d 100644 --- a/libraries/libstratosphere/include/stratosphere/tipc/tipc_object_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/tipc/tipc_object_manager.hpp @@ -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); } }; diff --git a/libraries/libstratosphere/include/stratosphere/tipc/tipc_server_manager.hpp b/libraries/libstratosphere/include/stratosphere/tipc/tipc_server_manager.hpp index 52418507f..7f15b2afb 100644 --- a/libraries/libstratosphere/include/stratosphere/tipc/tipc_server_manager.hpp +++ b/libraries/libstratosphere/include/stratosphere/tipc/tipc_server_manager.hpp @@ -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](std::index_sequence) ALWAYS_INLINE_LAMBDA { + (this->TriggerResumeImpl(resume_key), ...); + }(std::make_index_sequence()); + } private: template requires (Ix < NumPorts) void TryAllocateObject(size_t port_index, tipc::ServiceObjectBase *&allocated) { @@ -570,6 +582,17 @@ namespace ams::tipc { } } } + + template + void TriggerResumeImpl(ResumeKey resume_key) { + /* Get the port manager. */ + auto &port_manager = this->GetPortManager(); + + /* If we should, trigger a resume. */ + if (port_manager.TestResume(resume_key)) { + port_manager.TriggerResume(resume_key); + } + } }; template diff --git a/libraries/libstratosphere/source/_test/test_tipc_serializer.cpp b/libraries/libstratosphere/source/_test/test_tipc_serializer.cpp deleted file mode 100644 index 62daba4de..000000000 --- a/libraries/libstratosphere/source/_test/test_tipc_serializer.cpp +++ /dev/null @@ -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 . - */ -#include - -#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); - - 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); - - using UserInterfaceObject = ::ams::tipc::ServiceObject; - - using ManagerInterfaceObject = ::ams::tipc::ServiceObject; - - 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; - - namespace { - - TestServerManager g_test_server_manager; - - } - - void TestLoop() { - g_test_server_manager.LoopAuto(); - } - - -} \ No newline at end of file diff --git a/libraries/libstratosphere/source/os/os_message_queue.cpp b/libraries/libstratosphere/source/os/os_message_queue.cpp index 6e15ffdb2..e950c07b8 100644 --- a/libraries/libstratosphere/source/os/os_message_queue.cpp +++ b/libraries/libstratosphere/source/os/os_message_queue.cpp @@ -14,8 +14,9 @@ * along with this program. If not, see . */ #include -#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; + } + } diff --git a/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.cpp b/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.cpp index 7f94510c9..928ebc75f 100644 --- a/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.cpp +++ b/libraries/libstratosphere/source/settings/impl/settings_product_model_impl.cpp @@ -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)); } } diff --git a/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp b/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp index 3be077c81..7fcce4777 100644 --- a/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp +++ b/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp @@ -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; } diff --git a/stratosphere/sm/source/impl/sm_service_manager.cpp b/stratosphere/sm/source/impl/sm_service_manager.cpp index 7b480c4fe..8a8647c17 100644 --- a/stratosphere/sm/source/impl/sm_service_manager.cpp +++ b/stratosphere/sm/source/impl/sm_service_manager.cpp @@ -15,19 +15,21 @@ */ #include #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(e)), capacity(c) { + AccessControlEntry(const void *e, size_t c) : m_entry(static_cast(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(this->entry + 1), this->GetServiceNameSize()); + return ServiceName::Encode(reinterpret_cast(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 g_process_list = [] { + std::array 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 g_service_list = [] { + std::array list = {}; + /* Initialize each info. */ + for (auto &service_info : list) { + service_info = InvalidServiceInfo; + } + + return list; + }(); + + constinit std::array g_future_mitm_list = [] { + std::array 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(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(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(); } diff --git a/stratosphere/sm/source/impl/sm_service_manager.hpp b/stratosphere/sm/source/impl/sm_service_manager.hpp index cc628eec6..02ef3ed96 100644 --- a/stratosphere/sm/source/impl/sm_service_manager.hpp +++ b/stratosphere/sm/source/impl/sm_service_manager.hpp @@ -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(); diff --git a/stratosphere/sm/source/impl/sm_wait_list.cpp b/stratosphere/sm/source/impl/sm_wait_list.cpp deleted file mode 100644 index bb6a68cb4..000000000 --- a/stratosphere/sm/source/impl/sm_wait_list.cpp +++ /dev/null @@ -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 . - */ -#include -#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; - } - } - -} diff --git a/stratosphere/sm/source/sm_debug_monitor_service.cpp b/stratosphere/sm/source/sm_debug_monitor_service.cpp deleted file mode 100644 index 4622bbb4d..000000000 --- a/stratosphere/sm/source/sm_debug_monitor_service.cpp +++ /dev/null @@ -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 . - */ -#include -#include "sm_debug_monitor_service.hpp" -#include "impl/sm_service_manager.hpp" - -namespace ams::sm { - - Result DebugMonitorService::AtmosphereGetRecord(sf::Out record, ServiceName service) { - return impl::GetServiceRecord(record.GetPointer(), service); - } - - void DebugMonitorService::AtmosphereListRecords(const sf::OutArray &records, sf::Out out_count, u64 offset) { - R_ABORT_UNLESS(impl::ListServiceRecords(records.GetPointer(), out_count.GetPointer(), offset, records.GetSize())); - } - - void DebugMonitorService::AtmosphereGetRecordSize(sf::Out record_size) { - record_size.SetValue(sizeof(ServiceRecord)); - } - -} diff --git a/stratosphere/sm/source/sm_main.cpp b/stratosphere/sm/source/sm_main.cpp index 16070d43c..f72b2318a 100644 --- a/stratosphere/sm/source/sm_main.cpp +++ b/stratosphere/sm/source/sm_main.cpp @@ -14,11 +14,7 @@ * along with this program. If not, see . */ #include -#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 { - private: - virtual ams::Result OnNeedsToAccept(int port_index, Server *server) override; - }; - - using Allocator = sf::ExpHeapAllocator; - using ObjectFactory = sf::ObjectFactory; - - 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(std::addressof(g_server_allocator))); - case PortIndex_Manager: - return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(std::addressof(g_server_allocator))); - case PortIndex_DebugMonitor: - return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(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); diff --git a/stratosphere/sm/source/sm_manager_service.cpp b/stratosphere/sm/source/sm_manager_service.cpp index 0bc806933..f8755ecaa 100644 --- a/stratosphere/sm/source/sm_manager_service.cpp +++ b/stratosphere/sm/source/sm_manager_service.cpp @@ -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 out, ServiceName service) { + void ManagerService::AtmosphereHasMitm(tipc::Out 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()); } diff --git a/stratosphere/sm/source/sm_manager_service.hpp b/stratosphere/sm/source/sm_manager_service.hpp index db400ddd9..b8dcae1ff 100644 --- a/stratosphere/sm/source/sm_manager_service.hpp +++ b/stratosphere/sm/source/sm_manager_service.hpp @@ -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 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 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); diff --git a/stratosphere/sm/source/sm_tipc_server.cpp b/stratosphere/sm/source/sm_tipc_server.cpp new file mode 100644 index 000000000..9dc4dc897 --- /dev/null +++ b/stratosphere/sm/source/sm_tipc_server.cpp @@ -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 . + */ +#include +#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(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 + 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; + using ManagerPortMeta = tipc::PortMeta; + + /* Define server manager global. */ + using ServerManager = tipc::ServerManagerWithDeferral; + + 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(manager_port_handle); + g_server_manager.RegisterPort(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(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); + } + +} diff --git a/stratosphere/sm/source/sm_debug_monitor_service.hpp b/stratosphere/sm/source/sm_tipc_server.hpp similarity index 61% rename from stratosphere/sm/source/sm_debug_monitor_service.hpp rename to stratosphere/sm/source/sm_tipc_server.hpp index 3efff18a2..86dc9a92a 100644 --- a/stratosphere/sm/source/sm_debug_monitor_service.hpp +++ b/stratosphere/sm/source/sm_tipc_server.hpp @@ -13,19 +13,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once #include namespace ams::sm { - /* Service definition. */ - class DebugMonitorService { - public: - Result AtmosphereGetRecord(sf::Out record, ServiceName service); - void AtmosphereListRecords(const sf::OutArray &records, sf::Out out_count, u64 offset); - void AtmosphereGetRecordSize(sf::Out record_size); - }; - static_assert(sm::impl::IsIDebugMonitorInterface); + void InitializeTipcServer(); + void LoopProcessTipcServer(); } diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index f3b15295d..4a37f0ae7 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -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 client_info, sf::OutMoveHandle fwd_h, ServiceName service) { + Result UserService::AtmosphereAcknowledgeMitmSession(tipc::Out 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 out, ServiceName service) { + Result UserService::AtmosphereHasMitm(tipc::Out 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 out, ServiceName service) { + Result UserService::AtmosphereHasService(tipc::Out 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" diff --git a/stratosphere/sm/source/sm_user_service.hpp b/stratosphere/sm/source/sm_user_service.hpp index 544a7ca11..d6ea2ee23 100644 --- a/stratosphere/sm/source/sm_user_service.hpp +++ b/stratosphere/sm/source/sm_user_service.hpp @@ -13,7 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once #include @@ -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 client_info, sf::OutMoveHandle fwd_h, ServiceName service); - Result AtmosphereHasMitm(sf::Out out, ServiceName service); + Result AtmosphereAcknowledgeMitmSession(tipc::Out client_info, tipc::OutMoveHandle fwd_h, ServiceName service); + Result AtmosphereHasMitm(tipc::Out out, ServiceName service); Result AtmosphereWaitMitm(ServiceName service); Result AtmosphereDeclareFutureMitm(ServiceName service); Result AtmosphereClearFutureMitm(ServiceName service); - Result AtmosphereHasService(sf::Out out, ServiceName service); + Result AtmosphereHasService(tipc::Out 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); + /* TODO: static assert that this is a tipc interface with default prototyping. */ } diff --git a/stratosphere/sm/source/sm_user_service_cmif_shim.inc b/stratosphere/sm/source/sm_user_service_cmif_shim.inc new file mode 100644 index 000000000..92a4911b8 --- /dev/null +++ b/stratosphere/sm/source/sm_user_service_cmif_shim.inc @@ -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 . + */ + +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(message_buffer.GetBufferForDebug()); + u8 * const out_message_buffer = static_cast(message_buffer.GetBufferForDebug()); + + if (std::memcmp(raw_message_buffer, CmifExpectedRequestHeaderForRegisterClientAndDetachClient, sizeof(CmifExpectedRequestHeaderForRegisterClientAndDetachClient)) == 0) { + /* Get the command id. */ + const u32 command_id = *reinterpret_cast(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(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(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(); + } + +} diff --git a/stratosphere/sm/source/impl/sm_wait_list.hpp b/stratosphere/sm/source/sm_wait_list.hpp similarity index 67% rename from stratosphere/sm/source/impl/sm_wait_list.hpp rename to stratosphere/sm/source/sm_wait_list.hpp index 771503385..704bfb6d1 100644 --- a/stratosphere/sm/source/impl/sm_wait_list.hpp +++ b/stratosphere/sm/source/sm_wait_list.hpp @@ -16,14 +16,10 @@ #pragma once #include -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); }