mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-18 00:12:03 +00:00
tipc: implement framework/server support logic (except for actual processing)
This commit is contained in:
parent
e3a65b1405
commit
822875ecf5
7 changed files with 609 additions and 3 deletions
|
@ -19,3 +19,7 @@
|
||||||
#include <stratosphere/tipc/tipc_allocators.hpp>
|
#include <stratosphere/tipc/tipc_allocators.hpp>
|
||||||
|
|
||||||
#include <stratosphere/tipc/impl/tipc_impl_command_serialization.hpp>
|
#include <stratosphere/tipc/impl/tipc_impl_command_serialization.hpp>
|
||||||
|
#include <stratosphere/tipc/impl/tipc_autogen_interface_macros.hpp>
|
||||||
|
|
||||||
|
#include <stratosphere/tipc/tipc_object_manager.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_server_manager.hpp>
|
||||||
|
|
|
@ -127,7 +127,7 @@ namespace ams::tipc::impl {
|
||||||
static_assert(::NAMESPACE::Is##INTERFACE<ImplType>); \
|
static_assert(::NAMESPACE::Is##INTERFACE<ImplType>); \
|
||||||
\
|
\
|
||||||
/* Get accessor to the message buffer. */ \
|
/* Get accessor to the message buffer. */ \
|
||||||
svc::ipc::MessageBuffer message_buffer(svc::ipc::GetMessageBuffer()); \
|
const svc::ipc::MessageBuffer message_buffer(svc::ipc::GetMessageBuffer()); \
|
||||||
\
|
\
|
||||||
/* Get decision variables. */ \
|
/* Get decision variables. */ \
|
||||||
const auto tag = svc::ipc::MessageBuffer::MessageHeader(message_buffer).GetTag(); \
|
const auto tag = svc::ipc::MessageBuffer::MessageHeader(message_buffer).GetTag(); \
|
||||||
|
|
|
@ -25,8 +25,9 @@ namespace ams::tipc {
|
||||||
{ t.Allocate() } -> std::convertible_to<ServiceObjectBase *>;
|
{ t.Allocate() } -> std::convertible_to<ServiceObjectBase *>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T> requires IsServiceObject<T>
|
template<typename T, size_t N> requires IsServiceObject<T>
|
||||||
class SingletonAllocator final {
|
class SingletonAllocator final {
|
||||||
|
static_assert(N >= 1);
|
||||||
private:
|
private:
|
||||||
T m_singleton;
|
T m_singleton;
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_common.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_service_object.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_waitable_object.hpp>
|
||||||
|
|
||||||
|
namespace ams::tipc {
|
||||||
|
|
||||||
|
/* TODO: Put this in a better header. */
|
||||||
|
constexpr inline u16 MethodId_Invalid = 0x0;
|
||||||
|
constexpr inline u16 MethodId_CloseSession = 0xF;
|
||||||
|
|
||||||
|
class ObjectManagerBase {
|
||||||
|
private:
|
||||||
|
struct Entry {
|
||||||
|
util::TypedStorage<WaitableObject> object;
|
||||||
|
os::WaitableHolderType waitable_holder;
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
os::SdkMutex m_mutex{};
|
||||||
|
Entry *m_entries_start{};
|
||||||
|
Entry *m_entries_end{};
|
||||||
|
os::WaitableManagerType *m_waitable_manager{};
|
||||||
|
private:
|
||||||
|
Entry *FindEntry(svc::Handle handle) {
|
||||||
|
for (Entry *cur = m_entries_start; cur != m_entries_end; ++cur) {
|
||||||
|
if (GetReference(cur->object).GetHandle() == handle) {
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry *FindEntry(os::WaitableHolderType *holder) {
|
||||||
|
for (Entry *cur = m_entries_start; cur != m_entries_end; ++cur) {
|
||||||
|
if (std::addressof(cur->waitable_holder) == holder) {
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
constexpr ObjectManagerBase() = default;
|
||||||
|
|
||||||
|
void Initialize(os::WaitableManagerType *manager, Entry *entries, size_t max_objects) {
|
||||||
|
/* Set our waitable manager. */
|
||||||
|
m_waitable_manager = manager;
|
||||||
|
|
||||||
|
/* Setup entry pointers. */
|
||||||
|
m_entries_start = entries;
|
||||||
|
m_entries_end = entries + max_objects;
|
||||||
|
|
||||||
|
/* Construct all entries. */
|
||||||
|
for (size_t i = 0; i < max_objects; ++i) {
|
||||||
|
util::ConstructAt(m_entries_start[i].object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddObject(WaitableObject &object) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Find an empty entry. */
|
||||||
|
auto *entry = this->FindEntry(svc::InvalidHandle);
|
||||||
|
AMS_ABORT_UNLESS(entry != nullptr);
|
||||||
|
|
||||||
|
/* Set the entry's object. */
|
||||||
|
GetReference(entry->object) = object;
|
||||||
|
|
||||||
|
/* Setup the entry's holder. */
|
||||||
|
os::InitializeWaitableHolder(std::addressof(entry->waitable_holder), object.GetHandle());
|
||||||
|
os::LinkWaitableHolder(m_waitable_manager, std::addressof(entry->waitable_holder));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseObject(svc::Handle handle) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Find the matching entry. */
|
||||||
|
auto *entry = this->FindEntry(handle);
|
||||||
|
AMS_ABORT_UNLESS(entry != nullptr);
|
||||||
|
|
||||||
|
/* Finalize the entry's holder. */
|
||||||
|
os::UnlinkWaitableHolder(std::addressof(entry->waitable_holder));
|
||||||
|
os::FinalizeWaitableHolder(std::addressof(entry->waitable_holder));
|
||||||
|
|
||||||
|
/* Destroy the object. */
|
||||||
|
GetReference(entry->object).Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReplyAndReceive(os::WaitableHolderType **out_holder, WaitableObject *out_object, svc::Handle reply_target, os::WaitableManagerType *manager) {
|
||||||
|
/* Declare signaled holder for processing ahead of time. */
|
||||||
|
os::WaitableHolderType *signaled_holder;
|
||||||
|
|
||||||
|
/* Reply and receive until we get a newly signaled target. */
|
||||||
|
Result result = os::SdkReplyAndReceive(out_holder, reply_target, manager);
|
||||||
|
for (signaled_holder = *out_holder; signaled_holder == nullptr; signaled_holder = *out_holder) {
|
||||||
|
result = os::SdkReplyAndReceive(out_holder, svc::InvalidHandle, manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the entry matching the signaled holder. */
|
||||||
|
if (auto *entry = this->FindEntry(signaled_holder); entry != nullptr) {
|
||||||
|
/* Get the output object. */
|
||||||
|
*out_object = GetReference(entry->object);
|
||||||
|
*out_holder = nullptr;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Reply(svc::Handle reply_target) {
|
||||||
|
/* Perform the reply. */
|
||||||
|
s32 dummy;
|
||||||
|
R_TRY_CATCH(svc::ReplyAndReceive(std::addressof(dummy), nullptr, 0, reply_target, 0)) {
|
||||||
|
R_CATCH(svc::ResultTimedOut) {
|
||||||
|
/* Timing out is acceptable. */
|
||||||
|
return R_CURRENT_RESULT;
|
||||||
|
}
|
||||||
|
R_CATCH(svc::ResultSessionClosed) {
|
||||||
|
/* It's okay if we couldn't reply to a closed session. */
|
||||||
|
return R_CURRENT_RESULT;
|
||||||
|
}
|
||||||
|
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ProcessRequest(WaitableObject &object) {
|
||||||
|
/* Get the method id. */
|
||||||
|
const auto method_id = svc::ipc::MessageBuffer::MessageHeader(svc::ipc::MessageBuffer(svc::ipc::GetMessageBuffer())).GetTag();
|
||||||
|
|
||||||
|
/* Check that the method id is valid. */
|
||||||
|
R_UNLESS(method_id != MethodId_Invalid, tipc::ResultInvalidMethod());
|
||||||
|
|
||||||
|
/* If we're closing the object, do so. */
|
||||||
|
if (method_id == MethodId_CloseSession) {
|
||||||
|
const auto handle = object.GetHandle();
|
||||||
|
|
||||||
|
/* Close the object itself. */
|
||||||
|
this->CloseObject(handle);
|
||||||
|
|
||||||
|
/* Close the object's handle. */
|
||||||
|
/* NOTE: Nintendo does not check that this succeeds. */
|
||||||
|
R_ABORT_UNLESS(svc::CloseHandle(handle));
|
||||||
|
|
||||||
|
/* Return result to signify we closed the object. */
|
||||||
|
return tipc::ResultSessionClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process the generic method for the object. */
|
||||||
|
R_TRY(object.GetObject()->ProcessRequest());
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t MaxObjects>
|
||||||
|
class ObjectManager : public ObjectManagerBase {
|
||||||
|
private:
|
||||||
|
Entry m_entries_storage[MaxObjects]{};
|
||||||
|
public:
|
||||||
|
constexpr ObjectManager() = default;
|
||||||
|
|
||||||
|
void Initialize(os::WaitableManagerType *manager) {
|
||||||
|
this->Initialize(manager, m_entries_storage, MaxObjects);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,323 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_common.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_service_object.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_object_manager.hpp>
|
||||||
|
|
||||||
|
namespace ams::tipc {
|
||||||
|
|
||||||
|
template<size_t NumSessions, typename Interface, typename Impl, template<typename, size_t> typename _Allocator>
|
||||||
|
struct PortMeta {
|
||||||
|
static constexpr inline size_t MaxSessions = NumSessions;
|
||||||
|
|
||||||
|
using ServiceObject = tipc::ServiceObject<Interface, Impl>;
|
||||||
|
|
||||||
|
using Allocator = _Allocator<ServiceObject, NumSessions>;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DummyDeferralManager{
|
||||||
|
struct Key{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class PortManagerInterface {
|
||||||
|
public:
|
||||||
|
virtual Result ProcessRequest(WaitableObject &object) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename DeferralManagerType, size_t ThreadStackSize, typename... PortInfos>
|
||||||
|
class ServerManagerImpl {
|
||||||
|
private:
|
||||||
|
static_assert(util::IsAligned(ThreadStackSize, os::ThreadStackAlignment));
|
||||||
|
|
||||||
|
static constexpr inline bool IsDeferralSupported = !std::same_as<DeferralManagerType, DummyDeferralManager>;
|
||||||
|
using ResumeKey = typename DeferralManagerType::Key;
|
||||||
|
|
||||||
|
static ALWAYS_INLINE uintptr_t ConvertKeyToMessage(ResumeKey key) {
|
||||||
|
static_assert(sizeof(key) <= sizeof(uintptr_t));
|
||||||
|
static_assert(std::is_trivial<ResumeKey>::value);
|
||||||
|
|
||||||
|
/* TODO: std::bit_cast */
|
||||||
|
uintptr_t converted = 0;
|
||||||
|
std::memcpy(std::addressof(converted), std::addressof(key), sizeof(key));
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ALWAYS_INLINE ResumeKey ConvertMessageToKey(uintptr_t message) {
|
||||||
|
static_assert(sizeof(ResumeKey) <= sizeof(uintptr_t));
|
||||||
|
static_assert(std::is_trivial<ResumeKey>::value);
|
||||||
|
|
||||||
|
/* TODO: std::bit_cast */
|
||||||
|
ResumeKey converted = {};
|
||||||
|
std::memcpy(std::addressof(converted), std::addressof(message), sizeof(converted));
|
||||||
|
return converted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr inline size_t NumPorts = sizeof...(PortInfos);
|
||||||
|
static constexpr inline size_t MaxSessions = (PortInfos::MaxSessions + ...);
|
||||||
|
|
||||||
|
/* Verify that it's possible to service this many sessions, with our port manager count. */
|
||||||
|
static_assert(MaxSessions <= NumPorts * svc::ArgumentHandleCountMax);
|
||||||
|
|
||||||
|
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;
|
||||||
|
public:
|
||||||
|
class PortManagerBase : public PortManagerInterface {
|
||||||
|
public:
|
||||||
|
enum MessageType {
|
||||||
|
MessageType_AddSession = 0,
|
||||||
|
MessageType_TriggerResume = 1,
|
||||||
|
};
|
||||||
|
protected:
|
||||||
|
s32 m_id;
|
||||||
|
std::atomic<s32> m_num_sessions;
|
||||||
|
s32 m_port_number;
|
||||||
|
os::WaitableManagerType m_waitable_manager;
|
||||||
|
DeferralManagerType m_deferral_manager;
|
||||||
|
os::MessageQueueType m_message_queue;
|
||||||
|
os::WaitableHolderType m_message_queue_holder;
|
||||||
|
uintptr_t m_message_queue_storage[MaxSessions];
|
||||||
|
ObjectManagerBase *m_object_manager;
|
||||||
|
public:
|
||||||
|
PortManagerBase() : m_id(), m_num_sessions(), m_port_number(), m_waitable_manager(), m_deferral_manager(), m_message_queue(), m_message_queue_holder(), m_message_queue_storage(), m_object_manager() {
|
||||||
|
/* Setup our message queue. */
|
||||||
|
os::InitializeMessageQueue(std::addressof(m_message_queue), m_message_queue_storage, util::size(m_message_queue_storage));
|
||||||
|
os::InitializeWaitableHolder(std::addressof(m_message_queue_holder), std::addressof(m_message_queue), os::MessageQueueWaitType::ForNotEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeBase(s32 id, ObjectManagerBase *manager) {
|
||||||
|
/* Set our id. */
|
||||||
|
m_id = id;
|
||||||
|
|
||||||
|
/* Reset our session count. */
|
||||||
|
m_num_sessions = 0;
|
||||||
|
|
||||||
|
/* Initialize our waitable manager. */
|
||||||
|
os::InitializeWaitableManager(std::addressof(m_waitable_manager));
|
||||||
|
os::LinkWaitableHolder(std::addressof(m_waitable_manager), std::addressof(m_message_queue_holder));
|
||||||
|
|
||||||
|
/* Initialize our object manager. */
|
||||||
|
m_object_manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterPort(s32 index, svc::Handle port_handle) {
|
||||||
|
/* Set our port number. */
|
||||||
|
this->m_port_number = index;
|
||||||
|
|
||||||
|
/* Create a waitable object for the port. */
|
||||||
|
tipc::WaitableObject object;
|
||||||
|
|
||||||
|
/* Setup the object. */
|
||||||
|
object.InitializeAsPort(port_handle);
|
||||||
|
|
||||||
|
/* Register the object. */
|
||||||
|
m_object_manager->AddObject(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Result ProcessRequest(WaitableObject &object) override {
|
||||||
|
/* Process the request, this must succeed because we succeeded when deferring earlier. */
|
||||||
|
R_ABORT_UNLESS(m_object_manager->ProcessRequest(object));
|
||||||
|
|
||||||
|
/* NOTE: We support nested deferral, where Nintendo does not. */
|
||||||
|
if constexpr (IsDeferralSupported) {
|
||||||
|
R_UNLESS(!PortManagerBase::IsRequestDeferred(), tipc::ResultRequestDeferred());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reply to the request. */
|
||||||
|
return m_object_manager->Reply(object.GetHandle());
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReplyAndReceive(os::WaitableHolderType **out_holder, WaitableObject *out_object, svc::Handle reply_target) {
|
||||||
|
return m_object_manager->ReplyAndReceive(out_holder, out_object, reply_target, std::addressof(m_waitable_manager));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartRegisterRetry(ResumeKey key) {
|
||||||
|
/* Begin the retry. */
|
||||||
|
m_deferral_manager.StartRegisterRetry(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestResume(ResumeKey key) {
|
||||||
|
/* Check to see if the key corresponds to some deferred message. */
|
||||||
|
return m_deferral_manager.TestResume(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriggerResume(ResumeKey key) {
|
||||||
|
/* 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));
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
static bool IsRequestDeferred() {
|
||||||
|
if constexpr (IsDeferralSupported) {
|
||||||
|
/* Get the message buffer. */
|
||||||
|
const svc::ipc::MessageBuffer message_buffer(svc::ipc::GetMessageBuffer());
|
||||||
|
|
||||||
|
/* Parse the hipc headers. */
|
||||||
|
const svc::ipc::MessageBuffer::MessageHeader message_header(message_buffer);
|
||||||
|
const svc::ipc::MessageBuffer::SpecialHeader special_header(message_buffer, message_header);
|
||||||
|
|
||||||
|
/* Determine raw data index and extents. */
|
||||||
|
const auto raw_data_offset = message_buffer.GetRawDataIndex(message_header, special_header);
|
||||||
|
const auto raw_data_count = message_header.GetRawCount();
|
||||||
|
|
||||||
|
/* Result is the last raw data word. */
|
||||||
|
const Result method_result = message_buffer.GetRaw<u32>(raw_data_offset + raw_data_count - 1);
|
||||||
|
|
||||||
|
/* Check that the result is the special deferral result. */
|
||||||
|
return tipc::ResultRequestDeferred::Includes(method_result);
|
||||||
|
} else {
|
||||||
|
/* If deferral isn't supported, requests are never deferred. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename PortInfo, size_t PortSessions>
|
||||||
|
class PortManagerImpl final : public PortManagerBase {
|
||||||
|
private:
|
||||||
|
tipc::ObjectManager<PortSessions> m_object_manager_impl;
|
||||||
|
public:
|
||||||
|
PortManagerImpl() : PortManagerBase(), m_object_manager_impl() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(s32 id) {
|
||||||
|
/* Initialize our base. */
|
||||||
|
this->InitializeBase(id, std::addressof(m_object_manager_impl));
|
||||||
|
|
||||||
|
/* Initialize our object manager. */
|
||||||
|
m_object_manager_impl->Initialize(std::addressof(this->m_waitable_manager));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
using PortManager = PortManagerImpl<PortInfo<Ix>, SessionsPerPortManager<Ix>>;
|
||||||
|
|
||||||
|
using PortManagerTuple = decltype([]<size_t... Ix>(std::index_sequence<Ix...>) {
|
||||||
|
return std::tuple<PortManager<Ix>...>{};
|
||||||
|
}(std::make_index_sequence(NumPorts)));
|
||||||
|
|
||||||
|
using PortAllocatorTuple = std::tuple<typename PortInfos::Allocator...>;
|
||||||
|
private:
|
||||||
|
os::SdkMutex m_mutex;
|
||||||
|
os::TlsSlot m_tls_slot;
|
||||||
|
PortManagerTuple m_port_managers;
|
||||||
|
PortAllocatorTuple m_port_allocators;
|
||||||
|
os::ThreadType m_port_threads[NumPorts - 1];
|
||||||
|
alignas(os::ThreadStackAlignment) u8 m_port_stacks[ThreadStackSize * (NumPorts - 1)];
|
||||||
|
private:
|
||||||
|
template<size_t Ix>
|
||||||
|
ALWAYS_INLINE auto &GetPortManager() {
|
||||||
|
return std::get<Ix>(m_port_managers);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
ALWAYS_INLINE const auto &GetPortManager() const {
|
||||||
|
return std::get<Ix>(m_port_managers);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
void LoopAutoForPort() {
|
||||||
|
R_ABORT_UNLESS(this->LoopProcess(this->GetPortManager<Ix>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
static void LoopAutoForPortThreadFunction(void *_this) {
|
||||||
|
static_cast<ServerManagerImpl *>(_this)->LoopAutoForPort<Ix>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
void InitializePortThread(s32 priority) {
|
||||||
|
/* Create the thread. */
|
||||||
|
R_ABORT_UNLESS(os::CreateThread(m_port_threads + Ix, LoopAutoForPortThreadFunction, this, m_port_stacks + Ix, ThreadStackSize, priority));
|
||||||
|
|
||||||
|
/* Start the thread. */
|
||||||
|
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; }
|
||||||
|
|
||||||
|
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)), ...);
|
||||||
|
}(std::make_index_sequence(NumPorts));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Ix>
|
||||||
|
void RegisterPort(svc::Handle port_handle) {
|
||||||
|
this->GetPortManager<Ix>().RegisterPort(static_cast<s32>(Ix), port_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoopAuto() {
|
||||||
|
/* If we have additional threads, create and start them. */
|
||||||
|
if constexpr (NumPorts > 1) {
|
||||||
|
const auto thread_priority = os::GetThreadPriority(os::GetCurrentThread());
|
||||||
|
|
||||||
|
[thread_priority, this]<size_t... Ix>(std::index_sequence<Ix...>) ALWAYS_INLINE_LAMBDA {
|
||||||
|
/* Create all threads. */
|
||||||
|
(this->InitializePortThread<Ix>(thread_priority), ...);
|
||||||
|
}(std::make_index_sequence(NumPorts - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process for the last port. */
|
||||||
|
this->LoopAutoForPort<NumPorts - 1>();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
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. */
|
||||||
|
svc::Handle reply_target = svc::InvalidHandle;
|
||||||
|
while (true) {
|
||||||
|
/* TODO */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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...>;
|
||||||
|
|
||||||
|
template<size_t ThreadStackSize, typename... PortInfos>
|
||||||
|
using ServerManagerWithThreadStack = ServerManagerImpl<DummyDeferralManager, ThreadStackSize, PortInfos...>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_common.hpp>
|
||||||
|
#include <stratosphere/tipc/tipc_service_object.hpp>
|
||||||
|
|
||||||
|
namespace ams::tipc {
|
||||||
|
|
||||||
|
class WaitableObject {
|
||||||
|
public:
|
||||||
|
enum ObjectType : u8 {
|
||||||
|
ObjectType_Invalid = 0,
|
||||||
|
ObjectType_Port = 1,
|
||||||
|
ObjectType_Session = 2,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
svc::Handle m_handle;
|
||||||
|
ObjectType m_type;
|
||||||
|
bool m_managed;
|
||||||
|
tipc::ServiceObjectBase *m_object;
|
||||||
|
private:
|
||||||
|
void InitializeImpl(ObjectType type, svc::Handle handle, bool managed, tipc::ServiceObjectBase *object) {
|
||||||
|
/* Validate that the object isn't already constructed. */
|
||||||
|
AMS_ASSERT(m_type == ObjectType_Invalid);
|
||||||
|
|
||||||
|
/* Set all fields. */
|
||||||
|
m_handle = handle;
|
||||||
|
m_type = type;
|
||||||
|
m_managed = managed;
|
||||||
|
m_object = object;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
constexpr inline WaitableObject() : m_handle(svc::InvalidHandle), m_type(ObjectType_Invalid), m_managed(false), m_object(nullptr) { /* ... */ }
|
||||||
|
|
||||||
|
void InitializeAsPort(svc::Handle handle) {
|
||||||
|
/* NOTE: Nintendo sets ports as managed, but this will cause a nullptr-deref if one is ever closed. */
|
||||||
|
/* This is theoretically a non-issue, as ports can't be closed, but we will set ours as unmanaged, */
|
||||||
|
/* just in case. */
|
||||||
|
this->InitializeImpl(ObjectType_Port, handle, false, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeAsSession(svc::Handle handle, bool managed, tipc::ServiceObjectBase *object) {
|
||||||
|
this->InitializeImpl(ObjectType_Session, handle, managed, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Destroy() {
|
||||||
|
/* Validate that the object is constructed. */
|
||||||
|
AMS_ASSERT(m_type != ObjectType_Invalid);
|
||||||
|
|
||||||
|
/* If we're managed, destroy the associated object. */
|
||||||
|
if (m_managed) {
|
||||||
|
if (auto * const deleter = m_object->GetDeleter(); deleter != nullptr) {
|
||||||
|
deleter->DeleteServiceObject(m_object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset all fields. */
|
||||||
|
m_handle = svc::InvalidHandle;
|
||||||
|
m_type = ObjectType_Invalid;
|
||||||
|
m_managed = false;
|
||||||
|
m_object = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr svc::Handle GetHandle() const {
|
||||||
|
return m_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ObjectType GetType() const {
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr tipc::ServiceObjectBase *GetObject() const {
|
||||||
|
return m_object;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include <stratosphere/tipc/impl/tipc_autogen_interface_macros.hpp>
|
|
||||||
|
|
||||||
#define AMS_TEST_I_USER_INTERFACE_INTERFACE_INFO(C, H) \
|
#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, 0, Result, RegisterClient, (const tipc::ClientProcessId &client_process_id), (client_process_id)) \
|
||||||
|
|
Loading…
Reference in a new issue