diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp index 01648d3ec..f066b595c 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_auto_object.hpp @@ -177,10 +177,12 @@ namespace ams::kern { } }; - template + template requires std::derived_from class KScopedAutoObject { - static_assert(std::is_base_of::value); NON_COPYABLE(KScopedAutoObject); + private: + template + friend class KScopedAutoObject; private: T *obj; private: @@ -202,12 +204,32 @@ namespace ams::kern { this->obj = nullptr; } - constexpr ALWAYS_INLINE KScopedAutoObject(KScopedAutoObject &&rhs) { - this->obj = rhs.obj; - rhs.obj = nullptr; + template + constexpr ALWAYS_INLINE KScopedAutoObject(KScopedAutoObject &&rhs) { + if constexpr (std::same_as) { + this->obj = rhs.obj; + rhs.obj = nullptr; + } else { + T *derived = rhs.obj->template DynamicCast(); + if (derived == nullptr) { + rhs.obj->Close(); + } + + this->obj = derived; + rhs.obj = nullptr; + } } - constexpr ALWAYS_INLINE KScopedAutoObject &operator=(KScopedAutoObject &&rhs) { + template + constexpr ALWAYS_INLINE KScopedAutoObject &operator=(KScopedAutoObject &&rhs) { + if constexpr (!std::same_as) { + T *derived = rhs.obj->template DynamicCast(); + if (derived == nullptr) { + rhs.obj->Close(); + } + rhs.obj = nullptr; + } + rhs.Swap(*this); return *this; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp index 29768d908..a494e45c6 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp @@ -20,6 +20,10 @@ namespace ams::kern { class KPort; + class KSession; + class KClientSession; + class KLightSession; + class KLightClientSession; class KClientPort final : public KSynchronizationObject { MESOSPHERE_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject); @@ -33,6 +37,8 @@ namespace ams::kern { virtual ~KClientPort() { /* ... */ } void Initialize(KPort *parent, s32 max_sessions); + void OnSessionFinalized(); + void OnServerClosed(); constexpr const KPort *GetParent() const { return this->parent; } @@ -43,6 +49,7 @@ namespace ams::kern { virtual bool IsSignaled() const override; /* TODO: More of KClientPort. */ + Result CreateSession(KClientSession **out); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp index c6eef389d..025a2a7ef 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp @@ -39,6 +39,7 @@ namespace ams::kern { constexpr const KSession *GetParent() const { return this->parent; } /* TODO: More of KClientSession. */ + void OnServerClosed(); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp index 09ab27324..b57dd233c 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_object_name.hpp @@ -49,6 +49,11 @@ namespace ams::kern { return Delete(obj.GetPointerUnsafe(), name); } + + template requires std::derived_from + static KScopedAutoObject Find(const char *name) { + return Find(name); + } private: static KScopedAutoObject FindImpl(const char *name); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp index 65a4609ff..36406d405 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp @@ -22,6 +22,9 @@ namespace ams::kern { + class KServerSession; + class KLightServerSession; + class KPort final : public KAutoObjectWithSlabHeapAndContainer { MESOSPHERE_AUTOOBJECT_TRAITS(KPort, KAutoObject); private: @@ -50,7 +53,8 @@ namespace ams::kern { uintptr_t GetName() const { return this->name; } bool IsLight() const { return this->is_light; } - /* TODO: More of KPort */ + Result EnqueueSession(KServerSession *session); + Result EnqueueSession(KLightServerSession *session); KClientPort &GetClientPort() { return this->client; } KServerPort &GetServerPort() { return this->server; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_server_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_server_port.hpp index c8a429a5d..886171873 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_server_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_server_port.hpp @@ -38,6 +38,8 @@ namespace ams::kern { virtual ~KServerPort() { /* ... */ } void Initialize(KPort *parent); + void EnqueueSession(KServerSession *session); + void EnqueueSession(KLightServerSession *session); constexpr const KPort *GetParent() const { return this->parent; } @@ -47,7 +49,7 @@ namespace ams::kern { virtual void Destroy() override; virtual bool IsSignaled() const override; - /* TODO: More of KClientPort. */ + /* TODO: More of KServerPort. */ private: void CleanupSessions(); /* TODO: This is a placeholder definition. */ diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_server_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_server_session.hpp index 70d0583e9..5d26f3bb5 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_server_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_server_session.hpp @@ -36,13 +36,16 @@ namespace ams::kern { constexpr KServerSession() : parent(), request_list(), current_request(), lock() { /* ... */ } virtual ~KServerSession() { /* ... */ } - void Initialize(KSession *parent); + void Initialize(KSession *p) { this->parent = p; } constexpr const KSession *GetParent() const { return this->parent; } virtual bool IsSignaled() const override { MESOSPHERE_UNIMPLEMENTED(); } /* TODO: More of KServerSession. */ + Result OnRequest(KSessionRequest *request); + + void OnClientClosed(); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp index 0ee803b05..b67f81e3a 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_session.hpp @@ -51,12 +51,21 @@ namespace ams::kern { virtual ~KSession() { /* ... */ } + void Initialize(KClientPort *client_port, uintptr_t name); + virtual void Finalize() override; + virtual bool IsInitialized() const override { return this->initialized; } virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast(this->process); } static void PostDestroy(uintptr_t arg); - /* TODO: This is a placeholder definition. */ + void OnServerClosed(); + void OnClientClosed(); + + bool IsServerClosed() const { return this->state != State::Normal; } + bool IsClientClosed() const { return this->state != State::Normal; } + + Result OnRequest(KSessionRequest *request) { return this->server.OnRequest(request); } KClientSession &GetClientSession() { return this->client; } KServerSession &GetServerSession() { return this->server; } diff --git a/libraries/libmesosphere/source/kern_k_client_port.cpp b/libraries/libmesosphere/source/kern_k_client_port.cpp index 10eb84764..c548de1c2 100644 --- a/libraries/libmesosphere/source/kern_k_client_port.cpp +++ b/libraries/libmesosphere/source/kern_k_client_port.cpp @@ -25,6 +25,19 @@ namespace ams::kern { this->max_sessions = max_sessions; } + void KClientPort::OnSessionFinalized() { + KScopedSchedulerLock sl; + + const auto prev = this->num_sessions--; + if (prev == this->max_sessions) { + this->NotifyAvailable(); + } + } + + void KClientPort::OnServerClosed() { + MESOSPHERE_ASSERT_THIS(); + } + bool KClientPort::IsLight() const { return this->GetParent()->IsLight(); } @@ -43,4 +56,71 @@ namespace ams::kern { return this->num_sessions < this->max_sessions; } + Result KClientPort::CreateSession(KClientSession **out) { + MESOSPHERE_ASSERT_THIS(); + + /* Reserve a new session from the resource limit. */ + KScopedResourceReservation session_reservation(GetCurrentProcessPointer(), ams::svc::LimitableResource_SessionCountMax); + R_UNLESS(session_reservation.Succeeded(), svc::ResultLimitReached()); + + /* Update the session counts. */ + { + /* Atomically increment the number of sessions. */ + s32 new_sessions; + { + const auto max = this->max_sessions; + auto cur_sessions = this->num_sessions.load(std::memory_order_acquire); + do { + R_UNLESS(cur_sessions < max, svc::ResultOutOfSessions()); + new_sessions = cur_sessions + 1; + } while (!this->num_sessions.compare_exchange_weak(cur_sessions, new_sessions, std::memory_order_relaxed)); + + } + + /* Atomically update the peak session tracking. */ + { + auto peak = this->peak_sessions.load(std::memory_order_acquire); + do { + if (peak >= new_sessions) { + break; + } + } while (!this->peak_sessions.compare_exchange_weak(peak, new_sessions, std::memory_order_relaxed)); + } + } + + /* Create a new session. */ + KSession *session = KSession::Create(); + if (session == nullptr) { + /* Decrement the session count. */ + const auto prev = this->num_sessions--; + if (prev == this->max_sessions) { + this->NotifyAvailable(); + } + + return svc::ResultOutOfResource(); + } + + /* Initialize the session. */ + session->Initialize(this, this->parent->GetName()); + + /* Commit the session reservation. */ + session_reservation.Commit(); + + /* Register the session. */ + KSession::Register(session); + auto session_guard = SCOPE_GUARD { + session->GetClientSession().Close(); + session->GetServerSession().Close(); + }; + + /* Enqueue the session with our parent. */ + R_TRY(this->parent->EnqueueSession(std::addressof(session->GetServerSession()))); + + /* We succeeded, so set the output. */ + session_guard.Cancel(); + *out = std::addressof(session->GetClientSession()); + return ResultSuccess(); + + } + } diff --git a/libraries/libmesosphere/source/kern_k_client_session.cpp b/libraries/libmesosphere/source/kern_k_client_session.cpp new file mode 100644 index 000000000..9e2c9967b --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_client_session.cpp @@ -0,0 +1,24 @@ +/* + * 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 + +namespace ams::kern { + + void KClientSession::OnServerClosed() { + MESOSPHERE_ASSERT_THIS(); + } + +} diff --git a/libraries/libmesosphere/source/kern_k_port.cpp b/libraries/libmesosphere/source/kern_k_port.cpp index 9b081ed3c..a343a1887 100644 --- a/libraries/libmesosphere/source/kern_k_port.cpp +++ b/libraries/libmesosphere/source/kern_k_port.cpp @@ -34,11 +34,41 @@ namespace ams::kern { } void KPort::OnClientClosed() { - MESOSPHERE_UNIMPLEMENTED(); + MESOSPHERE_ASSERT_THIS(); + + KScopedSchedulerLock sl; + + if (this->state == State::Normal) { + this->state = State::ClientClosed; + } } void KPort::OnServerClosed() { - MESOSPHERE_UNIMPLEMENTED(); + MESOSPHERE_ASSERT_THIS(); + + KScopedSchedulerLock sl; + + if (this->state == State::Normal) { + this->state = State::ServerClosed; + } + } + + Result KPort::EnqueueSession(KServerSession *session) { + KScopedSchedulerLock sl; + + R_UNLESS(this->state == State::Normal, svc::ResultPortClosed()); + + this->server.EnqueueSession(session); + return ResultSuccess(); + } + + Result KPort::EnqueueSession(KLightServerSession *session) { + KScopedSchedulerLock sl; + + R_UNLESS(this->state == State::Normal, svc::ResultPortClosed()); + + this->server.EnqueueSession(session); + return ResultSuccess(); } } diff --git a/libraries/libmesosphere/source/kern_k_server_port.cpp b/libraries/libmesosphere/source/kern_k_server_port.cpp index 06096bc86..cbb8b93e1 100644 --- a/libraries/libmesosphere/source/kern_k_server_port.cpp +++ b/libraries/libmesosphere/source/kern_k_server_port.cpp @@ -89,7 +89,33 @@ namespace ams::kern { if (this->IsLight()) { return !this->light_session_list.empty(); } else { - return this->session_list.empty(); + return !this->session_list.empty(); + } + } + + void KServerPort::EnqueueSession(KServerSession *session) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(!this->IsLight()); + + KScopedSchedulerLock sl; + + /* Add the session to our queue. */ + this->session_list.push_back(*session); + if (this->session_list.size() == 1) { + this->NotifyAvailable(); + } + } + + void KServerPort::EnqueueSession(KLightServerSession *session) { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(this->IsLight()); + + KScopedSchedulerLock sl; + + /* Add the session to our queue. */ + this->light_session_list.push_back(*session); + if (this->light_session_list.size() == 1) { + this->NotifyAvailable(); } } diff --git a/libraries/libmesosphere/source/kern_k_server_session.cpp b/libraries/libmesosphere/source/kern_k_server_session.cpp new file mode 100644 index 000000000..cb1e8c69c --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_server_session.cpp @@ -0,0 +1,29 @@ +/* + * 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 + +namespace ams::kern { + + Result KServerSession::OnRequest(KSessionRequest *request) { + MESOSPHERE_UNIMPLEMENTED(); + } + + void KServerSession::OnClientClosed() { + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_UNIMPLEMENTED(); + } + +} diff --git a/libraries/libmesosphere/source/kern_k_session.cpp b/libraries/libmesosphere/source/kern_k_session.cpp new file mode 100644 index 000000000..416810b42 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_session.cpp @@ -0,0 +1,84 @@ +/* + * 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 + +namespace ams::kern { + + void KSession::Initialize(KClientPort *client_port, uintptr_t name) { + MESOSPHERE_ASSERT_THIS(); + + /* Increment reference count. */ + this->Open(); + + /* Create our sub sessions. */ + KAutoObject::Create(std::addressof(this->server)); + KAutoObject::Create(std::addressof(this->client)); + + /* Initialize our sub sessions. */ + this->server.Initialize(this); + this->client.Initialize(this); + + /* Set state and name. */ + this->state = State::Normal; + this->name = name; + + /* Set our owner process. */ + this->process = GetCurrentProcessPointer(); + this->process->Open(); + + /* Set our port. */ + this->port = port; + if (this->port != nullptr) { + this->port->Open(); + } + + /* Mark initialized. */ + this->initialized = true; + } + + void KSession::Finalize() { + if (this->port != nullptr) { + this->port->OnSessionFinalized(); + this->port->Close(); + } + } + + void KSession::OnServerClosed() { + MESOSPHERE_ASSERT_THIS(); + + if (this->state == State::Normal) { + this->state = State::ServerClosed; + this->client.OnServerClosed(); + } + } + + void KSession::OnClientClosed() { + MESOSPHERE_ASSERT_THIS(); + + if (this->state == State::Normal) { + this->state = State::ClientClosed; + this->server.OnClientClosed(); + } + } + + void KSession::PostDestroy(uintptr_t arg) { + /* Release the session count resource the owner process holds. */ + KProcess *owner = reinterpret_cast(arg); + owner->ReleaseResource(ams::svc::LimitableResource_SessionCountMax, 1); + owner->Close(); + } + +} diff --git a/libraries/libmesosphere/source/svc/kern_svc_port.cpp b/libraries/libmesosphere/source/svc/kern_svc_port.cpp index 2aa297d0e..5c2bce0a6 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_port.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_port.cpp @@ -77,12 +77,47 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result ConnectToNamedPort(ams::svc::Handle *out, KUserPointer user_name) { + /* Copy the provided name from user memory to kernel memory. */ + char name[KObjectName::NameLengthMax] = {}; + R_TRY(user_name.CopyStringTo(name, sizeof(name))); + + /* Validate that name is valid. */ + R_UNLESS(name[sizeof(name) - 1] == '\x00', svc::ResultOutOfRange()); + + MESOSPHERE_LOG("%s: ConnectToNamedPort(%s) was called\n", GetCurrentProcess().GetName(), name); + + /* Get the current handle table. */ + auto &handle_table = GetCurrentProcess().GetHandleTable(); + + /* Find the client port. */ + auto port = KObjectName::Find(name); + R_UNLESS(port.IsNotNull(), svc::ResultNotFound()); + + /* Reserve a handle for the port. */ + /* NOTE: Nintendo really does write directly to the output handle here. */ + R_TRY(handle_table.Reserve(out)); + auto handle_guard = SCOPE_GUARD { handle_table.Unreserve(*out); }; + + /* Create a session. */ + KClientSession *session; + R_TRY(port->CreateSession(std::addressof(session))); + + /* Register the session in the table, close the extra reference. */ + handle_table.Register(*out, session); + session->Close(); + + /* We succeeded. */ + handle_guard.Cancel(); + return ResultSuccess(); + } + } /* ============================= 64 ABI ============================= */ Result ConnectToNamedPort64(ams::svc::Handle *out_handle, KUserPointer name) { - MESOSPHERE_PANIC("Stubbed SvcConnectToNamedPort64 was called."); + return ConnectToNamedPort(out_handle, name); } Result CreatePort64(ams::svc::Handle *out_server_handle, ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ams::svc::Address name) { @@ -100,7 +135,7 @@ namespace ams::kern::svc { /* ============================= 64From32 ABI ============================= */ Result ConnectToNamedPort64From32(ams::svc::Handle *out_handle, KUserPointer name) { - MESOSPHERE_PANIC("Stubbed SvcConnectToNamedPort64From32 was called."); + return ConnectToNamedPort(out_handle, name); } Result CreatePort64From32(ams::svc::Handle *out_server_handle, ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ams::svc::Address name) {