tipc/sm: update more fully for 13.0.0 changes

This commit is contained in:
Michael Scire 2021-10-13 23:50:57 -07:00
parent ca25a884b5
commit 891fa32bf1
10 changed files with 357 additions and 309 deletions

View file

@ -44,12 +44,12 @@ namespace ams::tipc {
util::TypedStorage<T> storage;
};
private:
os::SdkMutex m_mutex;
Entry m_entries[N];
os::SdkMutex m_mutex;
public:
constexpr ALWAYS_INLINE SlabAllocator() : m_entries() { /* ... */ }
constexpr ALWAYS_INLINE SlabAllocator() : m_entries(), m_mutex() { /* ... */ }
ServiceObjectBase *Allocate() {
T *Allocate() {
std::scoped_lock lk(m_mutex);
for (size_t i = 0; i < N; ++i) {

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/tipc/tipc_object_holder.hpp>
#include <stratosphere/tipc/tipc_service_object_base.hpp>
namespace ams::tipc {
template<typename T>
concept IsResumeKey = util::is_pod<T>::value && (0 < sizeof(T) && sizeof(T) <= sizeof(uintptr_t));
template<IsResumeKey ResumeKey>
static constexpr ALWAYS_INLINE uintptr_t ConvertToInternalResumeKey(ResumeKey key) {
if constexpr (std::same_as<ResumeKey, uintptr_t>) {
return key;
} else if constexpr (sizeof(key) == sizeof(uintptr_t)) {
return std::bit_cast<uintptr_t>(key);
} else {
uintptr_t converted = 0;
std::memcpy(std::addressof(converted), std::addressof(key), sizeof(key));
return converted;
}
}
class DeferralManagerBase;
namespace impl {
class DeferrableBaseTag{};
}
class DeferrableBase : public impl::DeferrableBaseTag {
private:
DeferralManagerBase *m_deferral_manager;
ObjectHolder m_object_holder;
uintptr_t m_resume_key;
u8 m_message_buffer[svc::ipc::MessageBufferSize];
public:
ALWAYS_INLINE DeferrableBase() : m_deferral_manager(nullptr), m_object_holder(), m_resume_key() { /* ... */ }
~DeferrableBase();
ALWAYS_INLINE void SetDeferralManager(DeferralManagerBase *manager, os::NativeHandle reply_target, ServiceObjectBase *object) {
m_deferral_manager = manager;
m_object_holder.InitializeForDeferralManager(reply_target, object);
}
ALWAYS_INLINE bool TestResume(uintptr_t key) const {
return m_resume_key == key;
}
template<IsResumeKey ResumeKey>
ALWAYS_INLINE void RegisterRetry(ResumeKey key) {
m_resume_key = ConvertToInternalResumeKey(key);
std::memcpy(m_message_buffer, svc::ipc::GetMessageBuffer(), sizeof(m_message_buffer));
}
template<IsResumeKey ResumeKey, typename F>
ALWAYS_INLINE Result RegisterRetryIfDeferred(ResumeKey key, F f) {
const Result result = f();
if (tipc::ResultRequestDeferred::Includes(result)) {
this->RegisterRetry(key);
}
return result;
}
template<typename PortManager>
ALWAYS_INLINE void TriggerResume(PortManager *port_manager) {
/* Clear resume key. */
m_resume_key = 0;
/* Restore message buffer. */
std::memcpy(svc::ipc::GetMessageBuffer(), m_message_buffer, sizeof(m_message_buffer));
/* Process the request. */
return port_manager->ProcessDeferredRequest(m_object_holder);
}
};
template<class T>
concept IsDeferrable = std::derived_from<T, impl::DeferrableBaseTag>;
class DeferralManagerBase {
NON_COPYABLE(DeferralManagerBase);
NON_MOVEABLE(DeferralManagerBase);
private:
size_t m_object_count;
DeferrableBase *m_objects_base[0];
public:
ALWAYS_INLINE DeferralManagerBase() : m_object_count(0) { /* ... */ }
void AddObject(DeferrableBase &object, os::NativeHandle reply_target, ServiceObjectBase *service_object) {
/* Set ourselves as the manager for the object. */
object.SetDeferralManager(this, reply_target, service_object);
/* Add the object to our entries. */
AMS_ASSERT(m_object_count < N);
m_objects_base[m_object_count++] = std::addressof(object);
}
void RemoveObject(DeferrableBase *object) {
/* If the object is present, remove it. */
for (size_t i = 0; i < m_object_count; ++i) {
if (m_objects_base[i] == object) {
std::swap(m_objects_base[i], m_objects_base[--m_object_count]);
break;
}
}
}
ALWAYS_INLINE bool TestResume(uintptr_t resume_key) const {
/* Try to resume all entries. */
for (size_t i = 0; i < m_object_count; ++i) {
if (m_objects_base[i]->TestResume(resume_key)) {
return true;
}
}
return false;
}
template<typename PortManager>
ALWAYS_INLINE void TriggerResume(PortManager *port_manager, uintptr_t resume_key) const {
/* Try to resume all entries. */
for (size_t i = 0; i < m_object_count; ++i) {
if (m_objects_base[i]->TestResume(resume_key)) {
m_objects_base[i]->TriggerResume(port_manager);
}
}
}
protected:
static consteval size_t GetObjectPointersOffsetBase();
};
static_assert(std::is_standard_layout<DeferralManagerBase>::value);
inline DeferrableBase::~DeferrableBase() {
AMS_ASSUME(m_deferral_manager != nullptr);
m_deferral_manager->RemoveObject(this);
}
template<size_t N> requires (N > 0)
class DeferralManager final : public DeferralManagerBase {
private:
DeferrableBase *m_objects[N];
public:
DeferralManager();
private:
static consteval size_t GetObjectPointersOffset();
};
consteval size_t DeferralManagerBase::GetObjectPointersOffsetBase() {
return OFFSETOF(DeferralManagerBase, m_objects_base);
}
template<size_t N>
consteval size_t DeferralManager<N>::GetObjectPointersOffset() {
return OFFSETOF(DeferralManager<N>, m_objects);
}
template<size_t N>
inline DeferralManager<N>::DeferralManager() : DeferralManagerBase() {
static_assert(GetObjectPointersOffset() == GetObjectPointersOffsetBase());
static_assert(sizeof(DeferralManager<N>) == sizeof(DeferralManagerBase) + N * sizeof(DeferrableBase *));
}
}

View file

@ -16,7 +16,7 @@
#pragma once
#include <vapours.hpp>
#include <stratosphere/tipc/tipc_common.hpp>
#include <stratosphere/tipc/tipc_service_object.hpp>
#include <stratosphere/tipc/tipc_service_object_base.hpp>
namespace ams::tipc {
@ -26,6 +26,8 @@ namespace ams::tipc {
ObjectType_Invalid = 0,
ObjectType_Port = 1,
ObjectType_Session = 2,
ObjectType_Deferral = ObjectType_Invalid,
};
private:
os::NativeHandle m_handle;
@ -57,6 +59,10 @@ namespace ams::tipc {
this->InitializeImpl(ObjectType_Session, handle, managed, object);
}
void InitializeForDeferralManager(os::NativeHandle handle, tipc::ServiceObjectBase *object) {
this->InitializeImpl(ObjectType_Deferral, handle, false, object);
}
void Destroy() {
/* Validate that the object is constructed. */
AMS_ASSERT(m_type != ObjectType_Invalid);

View file

@ -125,7 +125,7 @@ namespace ams::tipc {
}
}
Result Reply(os::NativeHandle reply_target) {
void Reply(os::NativeHandle reply_target) {
/* Perform the reply. */
s32 dummy;
R_TRY_CATCH(svc::ReplyAndReceive(std::addressof(dummy), nullptr, 0, reply_target, 0)) {
@ -136,8 +136,6 @@ namespace ams::tipc {
/* It's okay if we couldn't reply to a closed session. */
}
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return ResultSuccess();
}
Result ProcessRequest(ObjectHolder &object) {

View file

@ -18,6 +18,7 @@
#include <stratosphere/tipc/tipc_common.hpp>
#include <stratosphere/tipc/tipc_service_object.hpp>
#include <stratosphere/tipc/tipc_object_manager.hpp>
#include <stratosphere/tipc/tipc_deferral_manager.hpp>
namespace ams::tipc {
@ -25,69 +26,51 @@ namespace ams::tipc {
struct PortMeta {
static constexpr inline size_t MaxSessions = NumSessions;
static constexpr bool CanDeferInvokeRequest = IsDeferrable<Impl>;
using ServiceObject = tipc::ServiceObject<Interface, Impl>;
using Allocator = _Allocator<ServiceObject, NumSessions>;
};
struct DummyDeferralManager{
struct Key{};
};
struct DummyDeferralManagerBase{};
class PortManagerInterface {
public:
virtual Result ProcessRequest(ObjectHolder &object) = 0;
};
template<size_t N>
struct DummyDeferralManager : public DummyDeferralManagerBase {};
template<typename DeferralManagerType, size_t ThreadStackSize, typename... PortInfos>
template<size_t ThreadStackSize, typename... PortInfos>
class ServerManagerImpl {
private:
static constexpr inline size_t NumPorts = sizeof...(PortInfos);
static constexpr inline size_t MaxSessions = (PortInfos::MaxSessions + ...);
/* Verify that we have at least one port. */
static_assert(NumPorts > 0);
/* Verify that it's possible to service this many sessions, with our port manager count. */
static_assert(MaxSessions <= NumPorts * svc::ArgumentHandleCountMax);
static_assert(util::IsAligned(ThreadStackSize, os::ThreadStackAlignment));
alignas(os::ThreadStackAlignment) static constinit inline u8 s_port_stacks[ThreadStackSize * (NumPorts - 1)];
static constexpr inline bool IsDeferralSupported = !std::same_as<DeferralManagerType, DummyDeferralManager>;
using ResumeKey = typename DeferralManagerType::Key;
static constexpr ALWAYS_INLINE uintptr_t ConvertKeyToMessage(ResumeKey key) {
static_assert(sizeof(key) <= sizeof(uintptr_t));
static_assert(std::is_trivial<ResumeKey>::value);
if constexpr (sizeof(key) == sizeof(uintptr_t)) {
return std::bit_cast<uintptr_t>(key);
} else {
uintptr_t converted = 0;
std::memcpy(std::addressof(converted), std::addressof(key), sizeof(key));
return converted;
}
}
static constexpr ALWAYS_INLINE ResumeKey ConvertMessageToKey(uintptr_t message) {
static_assert(sizeof(ResumeKey) <= sizeof(uintptr_t));
static_assert(std::is_trivial<ResumeKey>::value);
if constexpr (sizeof(ResumeKey) == sizeof(uintptr_t)) {
return std::bit_cast<ResumeKey>(message);
} else {
ResumeKey converted = {};
std::memcpy(std::addressof(converted), std::addressof(message), sizeof(converted));
return converted;
}
}
template<size_t Ix> requires (Ix < NumPorts)
static constexpr inline size_t SessionsPerPortManager = (Ix == NumPorts - 1) ? ((MaxSessions / NumPorts) + MaxSessions % NumPorts)
: ((MaxSessions / NumPorts));
template<size_t Ix> requires (Ix < NumPorts)
using PortInfo = typename std::tuple_element<Ix, std::tuple<PortInfos...>>::type;
static constexpr inline bool IsDeferralSupported = (PortInfos::CanDeferInvokeRequest || ...);
template<size_t Sessions>
using DeferralManagerImplType = typename std::conditional<IsDeferralSupported, DeferralManager<Sessions>, DummyDeferralManager<Sessions>>::type;
using DeferralManagerBaseType = typename std::conditional<IsDeferralSupported, DeferralManagerBase, DummyDeferralManagerBase>::type;
template<size_t Ix> requires (Ix < NumPorts)
static constexpr inline bool IsPortDeferrable = PortInfo<Ix>::CanDeferInvokeRequest;
public:
class PortManagerBase : public PortManagerInterface {
class PortManagerBase {
public:
enum MessageType : u8 {
MessageType_AddSession = 0,
@ -98,14 +81,14 @@ namespace ams::tipc {
std::atomic<s32> m_num_sessions;
s32 m_port_number;
os::MultiWaitType m_multi_wait;
DeferralManagerType m_deferral_manager;
os::MessageQueueType m_message_queue;
os::MultiWaitHolderType m_message_queue_holder;
uintptr_t m_message_queue_storage[MaxSessions];
ObjectManagerBase *m_object_manager;
ServerManagerImpl *m_server_manager;
ObjectManagerBase *m_object_manager;
DeferralManagerBaseType *m_deferral_manager;
public:
PortManagerBase() : m_id(), m_num_sessions(), m_port_number(), m_multi_wait(), m_deferral_manager(), m_message_queue(), m_message_queue_holder(), m_message_queue_storage(), m_object_manager(), m_server_manager() {
PortManagerBase() : m_id(), m_num_sessions(), m_port_number(), m_multi_wait(), m_message_queue(), m_message_queue_holder(), m_message_queue_storage(), m_server_manager(), m_object_manager(), m_deferral_manager() {
/* Setup our message queue. */
os::InitializeMessageQueue(std::addressof(m_message_queue), m_message_queue_storage, util::size(m_message_queue_storage));
os::InitializeMultiWaitHolder(std::addressof(m_message_queue_holder), std::addressof(m_message_queue), os::MessageQueueWaitType::ForNotEmpty);
@ -119,11 +102,7 @@ namespace ams::tipc {
return m_num_sessions;
}
ObjectManagerBase *GetObjectManager() const {
return m_object_manager;
}
void InitializeBase(s32 id, ServerManagerImpl *sm, ObjectManagerBase *manager) {
void InitializeBase(s32 id, ServerManagerImpl *sm, DeferralManagerBaseType *dm, ObjectManagerBase *om) {
/* Set our id. */
m_id = id;
@ -138,7 +117,10 @@ namespace ams::tipc {
os::LinkMultiWaitHolder(std::addressof(m_multi_wait), std::addressof(m_message_queue_holder));
/* Initialize our object manager. */
m_object_manager = manager;
m_object_manager = om;
/* Initialize our deferral manager. */
m_deferral_manager = dm;
}
void RegisterPort(s32 index, os::NativeHandle port_handle) {
@ -155,21 +137,57 @@ namespace ams::tipc {
m_object_manager->AddObject(object);
}
virtual Result ProcessRequest(ObjectHolder &object) override {
/* Process the request, this must succeed because we succeeded when deferring earlier. */
R_ABORT_UNLESS(m_object_manager->ProcessRequest(object));
os::NativeHandle ProcessRequest(ObjectHolder &object) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* NOTE: We support nested deferral, where Nintendo does not. */
if constexpr (IsDeferralSupported) {
R_UNLESS(!PortManagerBase::IsRequestDeferred(), tipc::ResultRequestDeferred());
/* Process the request. */
const Result result = m_object_manager->ProcessRequest(object);
if (R_SUCCEEDED(result)) {
/* We should reply only if the request isn't deferred. */
return !IsRequestDeferred() ? object.GetHandle() : os::InvalidNativeHandle;
} else {
/* Processing failed, so note the session as closed (or close it). */
this->CloseSessionIfNecessary(object, !tipc::ResultSessionClosed::Includes(result));
/* We shouldn't reply on failure. */
return os::InvalidNativeHandle;
}
/* Reply to the request. */
return m_object_manager->Reply(object.GetHandle());
}
Result ReplyAndReceive(os::MultiWaitHolderType **out_holder, ObjectHolder *out_object, os::NativeHandle reply_target) {
return m_object_manager->ReplyAndReceive(out_holder, out_object, reply_target, std::addressof(m_multi_wait));
template<bool Enable = IsDeferralSupported, typename = typename std::enable_if<Enable>::type>
void ProcessDeferredRequest(ObjectHolder &object) {
static_assert(Enable == IsDeferralSupported);
if (const auto reply_target = this->ProcessRequest(object); reply_target != os::InvalidNativeHandle) {
m_object_manager->Reply(reply_target);
}
}
bool ReplyAndReceive(os::MultiWaitHolderType **out_holder, ObjectHolder *out_object, os::NativeHandle reply_target) {
/* If we don't have a reply target, clear our message buffer. */
if (reply_target == os::InvalidNativeHandle) {
svc::ipc::MessageBuffer(svc::ipc::GetMessageBuffer()).SetNull();
}
/* Try to reply/receive. */
const Result result = m_object_manager->ReplyAndReceive(out_holder, out_object, reply_target, std::addressof(m_multi_wait));
/* Acquire exclusive access to the server manager. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* Handle the result. */
R_TRY_CATCH(result) {
R_CATCH(os::ResultSessionClosedForReceive, os::ResultReceiveListBroken) {
/* Close the object. */
this->CloseSession(*out_object);
/* We don't have anything to process. */
return false;
}
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
return true;
}
void AddSession(os::NativeHandle session_handle, tipc::ServiceObjectBase *service_object) {
@ -198,7 +216,7 @@ namespace ams::tipc {
const os::NativeHandle session_handle = static_cast<os::NativeHandle>(message_type >> BITSIZEOF(u32));
/* Allocate a service object for the port. */
auto *service_object = m_server_manager->AllocateObject(static_cast<size_t>(message_data));
auto *service_object = m_server_manager->AllocateObject(static_cast<size_t>(message_data), session_handle, *m_deferral_manager);
/* Add the newly-created service object. */
this->AddSession(session_handle, service_object);
@ -206,12 +224,8 @@ namespace ams::tipc {
break;
case MessageType_TriggerResume:
if constexpr (IsDeferralSupported) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* Perform the resume. */
const auto resume_key = ConvertMessageToKey(message_data);
m_deferral_manager.Resume(resume_key, this);
this->OnTriggerResume(message_data);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
@ -249,53 +263,28 @@ namespace ams::tipc {
--m_num_sessions;
}
Result StartRegisterRetry(ResumeKey key) {
if constexpr (IsDeferralSupported) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* Begin the retry. */
return m_deferral_manager.StartRegisterRetry(key);
} else {
return ResultSuccess();
}
}
void ProcessRegisterRetry(ObjectHolder &object) {
if constexpr (IsDeferralSupported) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* Process the retry. */
m_deferral_manager.ProcessRegisterRetry(object);
}
}
bool TestResume(ResumeKey key) {
bool TestResume(uintptr_t key) {
if constexpr (IsDeferralSupported) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* Check to see if the key corresponds to some deferred message. */
return m_deferral_manager.TestResume(key);
return m_deferral_manager->TestResume(key);
} else {
return false;
}
}
void TriggerResume(ResumeKey key) {
void TriggerResume(uintptr_t key) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* Send the key as a message. */
os::SendMessageQueue(std::addressof(m_message_queue), static_cast<uintptr_t>(MessageType_TriggerResume));
os::SendMessageQueue(std::addressof(m_message_queue), ConvertKeyToMessage(key));
os::SendMessageQueue(std::addressof(m_message_queue), key);
}
void TriggerAddSession(os::NativeHandle session_handle, size_t port_index) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* Increment our session count. */
++m_num_sessions;
@ -303,6 +292,14 @@ namespace ams::tipc {
os::SendMessageQueue(std::addressof(m_message_queue), static_cast<uintptr_t>(MessageType_AddSession) | (static_cast<u64>(session_handle) << BITSIZEOF(u32)));
os::SendMessageQueue(std::addressof(m_message_queue), static_cast<uintptr_t>(port_index));
}
private:
void OnTriggerResume(uintptr_t key) {
/* Acquire exclusive server manager access. */
std::scoped_lock lk(m_server_manager->GetMutex());
/* Trigger the resume. */
m_deferral_manager->TriggerResume(this, key);
}
public:
static bool IsRequestDeferred() {
if constexpr (IsDeferralSupported) {
@ -331,15 +328,16 @@ namespace ams::tipc {
template<typename PortInfo, size_t PortSessions>
class PortManagerImpl final : public PortManagerBase {
private:
DeferralManagerImplType<PortSessions> m_deferral_manager_impl;
tipc::ObjectManager<1 + PortSessions> m_object_manager_impl;
public:
PortManagerImpl() : PortManagerBase(), m_object_manager_impl() {
PortManagerImpl() : PortManagerBase(), m_deferral_manager_impl(), m_object_manager_impl() {
/* ... */
}
void Initialize(s32 id, ServerManagerImpl *sm) {
/* Initialize our base. */
this->InitializeBase(id, sm, std::addressof(m_object_manager_impl));
this->InitializeBase(id, sm, std::addressof(m_deferral_manager_impl), std::addressof(m_object_manager_impl));
/* Initialize our object manager. */
m_object_manager_impl.Initialize(std::addressof(this->m_multi_wait));
@ -356,7 +354,6 @@ namespace ams::tipc {
using PortAllocatorTuple = std::tuple<typename PortInfos::Allocator...>;
private:
os::SdkRecursiveMutex m_mutex;
os::TlsSlot m_tls_slot;
PortManagerTuple m_port_managers;
PortAllocatorTuple m_port_allocators;
os::ThreadType m_port_threads[NumPorts - 1];
@ -390,18 +387,11 @@ namespace ams::tipc {
os::StartThread(m_port_threads + Ix);
}
public:
ServerManagerImpl() : m_mutex(), m_tls_slot(), m_port_managers(), m_port_allocators() { /* ... */ }
os::TlsSlot GetTlsSlot() const { return m_tls_slot; }
ServerManagerImpl() : m_mutex(), m_port_managers(), m_port_allocators() { /* ... */ }
os::SdkRecursiveMutex &GetMutex() { return m_mutex; }
void Initialize() {
/* Initialize our tls slot. */
if constexpr (IsDeferralSupported) {
R_ABORT_UNLESS(os::SdkAllocateTlsSlot(std::addressof(m_tls_slot), nullptr));
}
/* Initialize our port managers. */
[this]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
(this->GetPortManager<Ix>().Initialize(static_cast<s32>(Ix), this), ...);
@ -438,14 +428,14 @@ namespace ams::tipc {
this->LoopAutoForPort<NumPorts - 1>();
}
tipc::ServiceObjectBase *AllocateObject(size_t port_index) {
tipc::ServiceObjectBase *AllocateObject(size_t port_index, os::NativeHandle handle, DeferralManagerBaseType &deferral_manager) {
/* Check that the port index is valid. */
AMS_ABORT_UNLESS(port_index < NumPorts);
/* Try to allocate from each port, in turn. */
tipc::ServiceObjectBase *allocated = nullptr;
[this, port_index, &allocated]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
(this->TryAllocateObject<Ix>(port_index, allocated), ...);
[this, port_index, handle, &deferral_manager, &allocated]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
(this->TryAllocateObject<Ix>(port_index, handle, deferral_manager, allocated), ...);
}(std::make_index_sequence<NumPorts>());
/* Return the allocated object. */
@ -453,13 +443,17 @@ namespace ams::tipc {
return allocated;
}
void TriggerResume(ResumeKey resume_key) {
template<IsResumeKey ResumeKey>
void TriggerResume(const ResumeKey &resume_key) {
/* Acquire exclusive access to ourselves. */
std::scoped_lock lk(m_mutex);
/* Convert to internal resume key. */
const auto internal_resume_key = ConvertToInternalResumeKey(resume_key);
/* Check/trigger resume on each of our ports. */
[this, resume_key]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
(this->TriggerResumeImpl<Ix>(resume_key), ...);
[this, internal_resume_key]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
(this->TriggerResumeImpl<Ix>(internal_resume_key), ...);
}(std::make_index_sequence<NumPorts>());
}
@ -485,50 +479,45 @@ namespace ams::tipc {
}
private:
template<size_t Ix> requires (Ix < NumPorts)
void TryAllocateObject(size_t port_index, tipc::ServiceObjectBase *&allocated) {
void TryAllocateObject(size_t port_index, os::NativeHandle handle, DeferralManagerBaseType &deferral_manager, tipc::ServiceObjectBase *&allocated) {
/* Check that the port index matches. */
if (port_index == Ix) {
/* Check that we haven't already allocated. */
AMS_ABORT_UNLESS(allocated == nullptr);
/* Get the allocator. */
auto &allocator = std::get<Ix>(m_port_allocators);
/* Allocate the object. */
AMS_ABORT_UNLESS(allocated == nullptr);
allocated = allocator.Allocate();
AMS_ABORT_UNLESS(allocated != nullptr);
auto * const new_object = allocator.Allocate();
AMS_ABORT_UNLESS(new_object != nullptr);
/* If we should, set the object's deleter. */
if constexpr (IsServiceObjectDeleter<typename std::tuple_element<Ix, PortAllocatorTuple>::type>) {
allocated->SetDeleter(std::addressof(allocator));
new_object->SetDeleter(std::addressof(allocator));
}
/* If we should, set the object's deferral manager. */
if constexpr (IsPortDeferrable<Ix>) {
deferral_manager.AddObject(new_object->GetImpl(), handle, new_object);
}
/* Set the allocated object. */
allocated = new_object;
}
}
Result LoopProcess(PortManagerBase &port_manager) {
/* Set our tls slot's value to be the port manager we're processing for. */
if constexpr (IsDeferralSupported) {
os::SetTlsValue(this->GetTlsSlot(), reinterpret_cast<uintptr_t>(std::addressof(port_manager)));
}
/* Clear the message buffer. */
/* NOTE: Nintendo only clears the hipc header. */
std::memset(svc::ipc::GetMessageBuffer(), 0, svc::ipc::MessageBufferSize);
/* Process requests forever. */
os::NativeHandle reply_target = os::InvalidNativeHandle;
while (true) {
/* Reply to our pending request, and receive a new one. */
/* Reply to our pending request, and wait to receive a new one. */
os::MultiWaitHolderType *signaled_holder = nullptr;
tipc::ObjectHolder signaled_object{};
R_TRY_CATCH(port_manager.ReplyAndReceive(std::addressof(signaled_holder), std::addressof(signaled_object), reply_target)) {
R_CATCH(os::ResultSessionClosedForReceive, os::ResultReceiveListBroken) {
/* Close the object and continue. */
port_manager.CloseSession(signaled_object);
/* We have nothing to reply to. */
reply_target = os::InvalidNativeHandle;
continue;
}
} R_END_TRY_CATCH;
while (!port_manager.ReplyAndReceive(std::addressof(signaled_holder), std::addressof(signaled_object), reply_target)) {
reply_target = os::InvalidNativeHandle;
signaled_object = {};
}
if (signaled_holder == nullptr) {
/* A session was signaled, accessible via signaled_object. */
@ -548,31 +537,7 @@ namespace ams::tipc {
case ObjectHolder::ObjectType_Session:
{
/* Process the request */
const Result process_result = port_manager.GetObjectManager()->ProcessRequest(signaled_object);
if (R_SUCCEEDED(process_result)) {
if constexpr (IsDeferralSupported) {
/* Check if the request is deferred. */
if (PortManagerBase::IsRequestDeferred()) {
/* Process the retry that we began. */
port_manager.ProcessRegisterRetry(signaled_object);
/* We have nothing to reply to. */
reply_target = os::InvalidNativeHandle;
} else {
/* We're done processing, so we should reply. */
reply_target = signaled_object.GetHandle();
}
} else {
/* We're done processing, so we should reply. */
reply_target = signaled_object.GetHandle();
}
} else {
/* We failed to process, so note the session as closed (or close it). */
port_manager.CloseSessionIfNecessary(signaled_object, !tipc::ResultSessionClosed::Includes(process_result));
/* We have nothing to reply to. */
reply_target = os::InvalidNativeHandle;
}
reply_target = port_manager.ProcessRequest(signaled_object);
}
break;
AMS_UNREACHABLE_DEFAULT_CASE();
@ -625,7 +590,7 @@ namespace ams::tipc {
}
template<size_t Ix>
void TriggerResumeImpl(ResumeKey resume_key) {
void TriggerResumeImpl(uintptr_t resume_key) {
/* Get the port manager. */
auto &port_manager = this->GetPortManager<Ix>();
@ -636,17 +601,11 @@ namespace ams::tipc {
}
};
template<typename DeferralManagerType, typename... PortInfos>
using ServerManagerWithDeferral = ServerManagerImpl<DeferralManagerType, os::MemoryPageSize, PortInfos...>;
template<typename DeferralManagerType, size_t ThreadStackSize, typename... PortInfos>
using ServerManagerWithDeferralAndThreadStack = ServerManagerImpl<DeferralManagerType, ThreadStackSize, PortInfos...>;
template<typename... PortInfos>
using ServerManager = ServerManagerImpl<DummyDeferralManager, os::MemoryPageSize, PortInfos...>;
using ServerManager = ServerManagerImpl<os::MemoryPageSize, PortInfos...>;
template<size_t ThreadStackSize, typename... PortInfos>
using ServerManagerWithThreadStack = ServerManagerImpl<DummyDeferralManager, ThreadStackSize, PortInfos...>;
using ServerManagerWithThreadStack = ServerManagerImpl<ThreadStackSize, PortInfos...>;
}

View file

@ -604,11 +604,10 @@ namespace ams::sm::impl {
bool has_service = false;
R_TRY(impl::HasService(std::addressof(has_service), service));
/* If we do, we can succeed immediately. */
R_SUCCEED_IF(has_service);
/* If we don't, we want to wait until the service is registered. */
R_UNLESS(has_service, tipc::ResultRequestDeferred());
/* Otherwise, we want to wait until the service is registered. */
return StartRegisterRetry(service);
return ResultSuccess();
}
Result GetServiceHandle(os::NativeHandle *out, os::ProcessId process_id, ServiceName service) {
@ -628,9 +627,10 @@ namespace ams::sm::impl {
/* Get service info. Check to see if we need to defer this until later. */
ServiceInfo *service_info = GetServiceInfo(service);
MitmInfo *mitm_info = GetMitmInfo(service_info);
if (service_info == nullptr || ShouldDeferForInit(service) || HasFutureMitmDeclaration(service) || (mitm_info != nullptr && mitm_info->waiting_ack)) {
return StartRegisterRetry(service);
}
R_UNLESS(service_info != nullptr, tipc::ResultRequestDeferred());
R_UNLESS(!ShouldDeferForInit(service), tipc::ResultRequestDeferred());
R_UNLESS(!HasFutureMitmDeclaration(service), tipc::ResultRequestDeferred());
R_UNLESS((mitm_info == nullptr || !mitm_info->waiting_ack), tipc::ResultRequestDeferred());
/* Get a handle from the service info. */
R_TRY_CATCH(GetServiceHandleImpl(out, service_info, process_id)) {
@ -715,11 +715,10 @@ namespace ams::sm::impl {
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);
/* If we don't, we want to wait until the mitm is installed. */
R_UNLESS(has_mitm, tipc::ResultRequestDeferred());
/* Otherwise, we want to wait until the service is registered. */
return StartRegisterRetry(service);
return ResultSuccess();
}
Result InstallMitm(os::NativeHandle *out, os::NativeHandle *out_query, os::ProcessId process_id, ServiceName service) {
@ -740,7 +739,7 @@ namespace ams::sm::impl {
ServiceInfo *service_info = GetServiceInfo(service);
/* If it doesn't exist, defer until it does. */
R_UNLESS(service_info != nullptr, StartRegisterRetry(service));
R_UNLESS(service_info != nullptr, tipc::ResultRequestDeferred());
/* Validate that the service isn't already being mitm'd. */
R_UNLESS(GetMitmInfo(service_info) == nullptr, sm::ResultAlreadyRegistered());

View file

@ -40,99 +40,12 @@ namespace ams::sm {
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::ObjectHolder 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::ObjectHolder &object) {
/* Verify that we have a processing entry. */
AMS_ABORT_UNLESS(m_processing_entry != nullptr);
/* Set the entry's object. */
m_processing_entry->object = object;
/* Clear our processing entry. */
m_processing_entry = nullptr;
}
bool TestResume(sm::ServiceName service_name) {
/* Check that we have a matching service name. */
for (const auto &entry : m_entries) {
if (entry.service_name == service_name) {
return true;
}
}
return false;
}
template<typename PortManagerType>
void Resume(sm::ServiceName service_name, PortManagerType *port_manager) {
/* Resume (and clear) all matching entries. */
for (auto &entry : m_entries) {
if (entry.service_name == service_name) {
/* Copy the saved message buffer. */
std::memcpy(svc::ipc::GetMessageBuffer(), entry.message_buffer, svc::ipc::MessageBufferSize);
/* Resume the request. */
R_TRY_CATCH(port_manager->ProcessRequest(entry.object)) {
R_CATCH(tipc::ResultRequestDeferred) {
this->ProcessRegisterRetry(entry.object);
}
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
/* Clear the entry's service name. */
entry.service_name = sm::InvalidServiceName;
}
}
}
};
/* Define port metadata. */
using UserPortMeta = tipc::PortMeta<MaxSessionsUser, impl::IUserInterface, UserService, tipc::SlabAllocator>;
using ManagerPortMeta = tipc::PortMeta<MaxSessionsManager, impl::IManagerInterface, ManagerService, tipc::SingletonAllocator>;
using UserPortMeta = tipc::PortMeta<MaxSessionsUser, impl::IUserInterface, UserService, tipc::SlabAllocator>;
using ManagerPortMeta = tipc::PortMeta<MaxSessionsManager, impl::IManagerInterface, ManagerService, tipc::SingletonAllocator>;
/* Define server manager global. */
using ServerManager = tipc::ServerManagerWithDeferral<sm::WaitList, ManagerPortMeta, UserPortMeta>;
using ServerManager = tipc::ServerManager<ManagerPortMeta, UserPortMeta>;
ServerManager g_server_manager;
@ -162,14 +75,6 @@ namespace ams::sm {
g_server_manager.LoopAuto();
}
Result StartRegisterRetry(sm::ServiceName service_name) {
/* Get the port manager from where it's saved in TLS. */
auto *port_manager = reinterpret_cast<typename ServerManager::PortManagerBase *>(os::GetTlsValue(g_server_manager.GetTlsSlot()));
/* Register the retry. */
return port_manager->StartRegisterRetry(service_name);
}
void TriggerResume(sm::ServiceName service_name) {
/* Trigger a resumption. */
g_server_manager.TriggerResume(service_name);

View file

@ -143,7 +143,7 @@ namespace ams::sm {
const Result result = this->GetServiceHandle(std::addressof(out_handle), service_name);
/* Serialize output. */
if (R_SUCCEEDED(result) || !tipc::ResultRequestDeferred::Includes(result)) {
if (!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));
@ -155,12 +155,8 @@ namespace ams::sm {
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));
}
std::memcpy(out_message_buffer + 0x00, CmifResponseToUnregisterService, sizeof(CmifResponseToUnregisterService));
std::memcpy(out_message_buffer + 0x18, std::addressof(result), sizeof(result));
} else {
return tipc::ResultInvalidMethod();
}
@ -186,13 +182,9 @@ namespace ams::sm {
const Result result = this->RegisterService(std::addressof(out_handle), service_name, max_sessions, is_light);
/* 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));
}
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 {
return tipc::ResultInvalidMethod();
}

View file

@ -20,13 +20,13 @@
namespace ams::sm {
/* Service definition. */
class UserService {
class UserService : public tipc::DeferrableBase {
private:
os::ProcessId m_process_id;
bool m_initialized;
public:
constexpr UserService() : m_process_id{os::InvalidProcessId}, m_initialized{false} { /* ... */ }
virtual ~UserService() {
UserService() : m_process_id{os::InvalidProcessId}, m_initialized{false} { /* ... */ }
~UserService() {
if (m_initialized) {
impl::OnClientDisconnected(m_process_id);
}
@ -41,7 +41,9 @@ namespace ams::sm {
Result GetServiceHandle(tipc::OutMoveHandle out_h, ServiceName service) {
R_UNLESS(m_initialized, sm::ResultInvalidClient());
return impl::GetServiceHandle(out_h.GetPointer(), m_process_id, service);
return this->RegisterRetryIfDeferred(service, [&] ALWAYS_INLINE_LAMBDA () -> Result {
return impl::GetServiceHandle(out_h.GetPointer(), m_process_id, service);
});
}
Result RegisterService(tipc::OutMoveHandle out_h, ServiceName service, u32 max_sessions, bool is_light) {
@ -64,7 +66,9 @@ namespace ams::sm {
/* Atmosphere commands. */
Result AtmosphereInstallMitm(tipc::OutMoveHandle srv_h, tipc::OutMoveHandle qry_h, ServiceName service) {
R_UNLESS(m_initialized, sm::ResultInvalidClient());
return impl::InstallMitm(srv_h.GetPointer(), qry_h.GetPointer(), m_process_id, service);
return this->RegisterRetryIfDeferred(service, [&] ALWAYS_INLINE_LAMBDA () -> Result {
return impl::InstallMitm(srv_h.GetPointer(), qry_h.GetPointer(), m_process_id, service);
});
}
Result AtmosphereUninstallMitm(ServiceName service) {
@ -84,7 +88,9 @@ namespace ams::sm {
Result AtmosphereWaitMitm(ServiceName service) {
R_UNLESS(m_initialized, sm::ResultInvalidClient());
return impl::WaitMitm(service);
return this->RegisterRetryIfDeferred(service, [&] ALWAYS_INLINE_LAMBDA () -> Result {
return impl::WaitMitm(service);
});
}
Result AtmosphereDeclareFutureMitm(ServiceName service) {
@ -104,13 +110,16 @@ namespace ams::sm {
Result AtmosphereWaitService(ServiceName service) {
R_UNLESS(m_initialized, sm::ResultInvalidClient());
return impl::WaitService(service);
return this->RegisterRetryIfDeferred(service, [&] ALWAYS_INLINE_LAMBDA () -> Result {
return impl::WaitService(service);
});
}
public:
/* Backwards compatibility layer for cmif. */
Result ProcessDefaultServiceCommand(const svc::ipc::MessageBuffer &message_buffer);
};
static_assert(sm::impl::IsIUserInterface<UserService>);
static_assert(tipc::IsDeferrable<UserService>);
/* TODO: static assert that this is a tipc interface with default prototyping. */
}

View file

@ -18,8 +18,6 @@
namespace ams::sm {
Result StartRegisterRetry(sm::ServiceName service_name);
void TriggerResume(sm::ServiceName service_name);
}