From 93be2ffcbaf3887c7e48c74fab409d1c92434995 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 14 Jul 2020 02:24:26 -0700 Subject: [PATCH] kern: add SvcCreatePort, SvcConnectToPort --- .../include/mesosphere/kern_k_client_port.hpp | 1 + .../mesosphere/kern_k_client_session.hpp | 3 +- .../kern_k_light_client_session.hpp | 5 +- .../kern_k_light_server_session.hpp | 13 ++- .../mesosphere/kern_k_light_session.hpp | 11 ++- .../source/kern_k_client_port.cpp | 65 +++++++++++++ .../source/kern_k_light_client_session.cpp | 36 ++++++++ .../source/kern_k_light_server_session.cpp | 44 +++++++++ .../source/kern_k_light_session.cpp | 91 +++++++++++++++++++ .../source/svc/kern_svc_port.cpp | 80 +++++++++++++++- 10 files changed, 338 insertions(+), 11 deletions(-) create mode 100644 libraries/libmesosphere/source/kern_k_light_client_session.cpp create mode 100644 libraries/libmesosphere/source/kern_k_light_server_session.cpp create mode 100644 libraries/libmesosphere/source/kern_k_light_session.cpp diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp index a494e45c6..c647830bc 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_port.hpp @@ -50,6 +50,7 @@ namespace ams::kern { /* TODO: More of KClientPort. */ Result CreateSession(KClientSession **out); + Result CreateLightSession(KLightClientSession **out); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp index 6998af64f..953e247e1 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp @@ -29,13 +29,12 @@ namespace ams::kern { constexpr KClientSession() : parent() { /* ... */ } virtual ~KClientSession() { /* ... */ } - virtual void Destroy() override; - void Initialize(KSession *parent) { /* Set member variables. */ this->parent = parent; } + virtual void Destroy() override; static void PostDestroy(uintptr_t arg) { /* ... */ } constexpr KSession *GetParent() const { return this->parent; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp index 474ae35e5..f8c429bce 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp @@ -34,11 +34,14 @@ namespace ams::kern { this->parent = parent; } + virtual void Destroy() override; static void PostDestroy(uintptr_t arg) { /* ... */ } constexpr const KLightSession *GetParent() const { return this->parent; } - /* TODO: More of KLightClientSession. */ + Result SendSyncRequest(u32 *data); + + void OnServerClosed(); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp index 7fcb23a13..1487a714d 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp @@ -35,13 +35,22 @@ namespace ams::kern { constexpr KLightServerSession() : parent(), request_queue(), server_queue(), current_request(), server_thread() { /* ... */ } virtual ~KLightServerSession() { /* ... */ } - void Initialize(KLightSession *parent); + void Initialize(KLightSession *parent) { + /* Set member variables. */ + this->parent = parent; + } + virtual void Destroy() override; static void PostDestroy(uintptr_t arg) { /* ... */ } constexpr const KLightSession *GetParent() const { return this->parent; } - /* TODO: More of KLightServerSession. */ + Result OnRequest(KThread *request_thread); + Result ReplyAndReceive(u32 *data); + + void OnClientClosed(); + private: + void CleanupRequests(); }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp index b4257a150..2e3ad41c1 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp @@ -51,12 +51,21 @@ namespace ams::kern { virtual ~KLightSession() { /* ... */ } + 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(KThread *request_thread) { return this->server.OnRequest(request_thread); } KLightClientSession &GetClientSession() { return this->client; } KLightServerSession &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 c548de1c2..44fd08e4c 100644 --- a/libraries/libmesosphere/source/kern_k_client_port.cpp +++ b/libraries/libmesosphere/source/kern_k_client_port.cpp @@ -120,7 +120,72 @@ namespace ams::kern { session_guard.Cancel(); *out = std::addressof(session->GetClientSession()); return ResultSuccess(); + } + Result KClientPort::CreateLightSession(KLightClientSession **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. */ + KLightSession *session = KLightSession::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. */ + KLightSession::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_light_client_session.cpp b/libraries/libmesosphere/source/kern_k_light_client_session.cpp new file mode 100644 index 000000000..b2c5686ef --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_light_client_session.cpp @@ -0,0 +1,36 @@ +/* + * 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 KLightClientSession::Destroy() { + MESOSPHERE_ASSERT_THIS(); + + this->parent->OnClientClosed(); + } + + void KLightClientSession::OnServerClosed() { + MESOSPHERE_ASSERT_THIS(); + } + + Result KLightClientSession::SendSyncRequest(u32 *data) { + MESOSPHERE_ASSERT_THIS(); + + MESOSPHERE_UNIMPLEMENTED(); + } + +} diff --git a/libraries/libmesosphere/source/kern_k_light_server_session.cpp b/libraries/libmesosphere/source/kern_k_light_server_session.cpp new file mode 100644 index 000000000..c3995191c --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_light_server_session.cpp @@ -0,0 +1,44 @@ +/* + * 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 KLightServerSession::Destroy() { + MESOSPHERE_ASSERT_THIS(); + + this->CleanupRequests(); + + this->parent->OnServerClosed(); + } + + void KLightServerSession::OnClientClosed() { + MESOSPHERE_ASSERT_THIS(); + } + + Result KLightServerSession::OnRequest(KThread *request_thread) { + MESOSPHERE_UNIMPLEMENTED(); + } + + Result KLightServerSession::ReplyAndReceive(u32 *data) { + MESOSPHERE_UNIMPLEMENTED(); + } + + void KLightServerSession::CleanupRequests() { + MESOSPHERE_UNIMPLEMENTED(); + } + +} diff --git a/libraries/libmesosphere/source/kern_k_light_session.cpp b/libraries/libmesosphere/source/kern_k_light_session.cpp new file mode 100644 index 000000000..6132d09b7 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_light_session.cpp @@ -0,0 +1,91 @@ +/* + * 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 KLightSession::Initialize(KClientPort *client_port, uintptr_t name) { + MESOSPHERE_ASSERT_THIS(); + + /* Increment reference count. */ + /* Because reference count is one on creation, this will result */ + /* in a reference count of two. Thus, when both server and client are closed */ + /* this object will be destroyed. */ + 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 = client_port; + if (this->port != nullptr) { + this->port->Open(); + } + + /* Mark initialized. */ + this->initialized = true; + } + + void KLightSession::Finalize() { + if (this->port != nullptr) { + this->port->OnSessionFinalized(); + this->port->Close(); + } + } + + void KLightSession::OnServerClosed() { + MESOSPHERE_ASSERT_THIS(); + + if (this->state == State::Normal) { + this->state = State::ServerClosed; + this->client.OnServerClosed(); + } + + this->Close(); + } + + void KLightSession::OnClientClosed() { + MESOSPHERE_ASSERT_THIS(); + + if (this->state == State::Normal) { + this->state = State::ClientClosed; + this->server.OnClientClosed(); + } + + this->Close(); + } + + void KLightSession::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 5c2bce0a6..3bc7c3e78 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_port.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_port.cpp @@ -47,7 +47,7 @@ namespace ams::kern::svc { port->Initialize(max_sessions, false, 0); /* Register the port. */ - KPort::Register(port); + R_TRY(KPort::Register(port)); /* Register the handle in the table. */ handle_table.Register(*out_server_handle, std::addressof(port->GetServerPort())); @@ -77,6 +77,43 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result CreatePort(ams::svc::Handle *out_server, ams::svc::Handle *out_client, int32_t max_sessions, bool is_light, uintptr_t name) { + /* Ensure max sessions is valid. */ + R_UNLESS(max_sessions > 0, svc::ResultOutOfRange()); + + /* Get the current handle table. */ + auto &handle_table = GetCurrentProcess().GetHandleTable(); + + /* Create a new port. */ + KPort *port = KPort::Create(); + R_UNLESS(port != nullptr, svc::ResultOutOfResource()); + + /* Initialize the port. */ + port->Initialize(max_sessions, is_light, name); + + /* Ensure that we clean up the port (and its only references are handle table) on function end. */ + ON_SCOPE_EXIT { + port->GetServerPort().Close(); + port->GetClientPort().Close(); + }; + + /* Register the port. */ + R_TRY(KPort::Register(port)); + + /* Add the client to the handle table. */ + R_TRY(handle_table.Add(out_client, std::addressof(port->GetClientPort()))); + + /* Ensure that we maintaing a clean handle state on exit. */ + auto handle_guard = SCOPE_GUARD { handle_table.Remove(*out_client); }; + + /* Add the server to the handle table. */ + R_TRY(handle_table.Add(out_server, std::addressof(port->GetServerPort()))); + + /* We succeeded! */ + handle_guard.Cancel(); + 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] = {}; @@ -112,6 +149,39 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result ConnectToPort(ams::svc::Handle *out, ams::svc::Handle port) { + /* Get the current handle table. */ + auto &handle_table = GetCurrentProcess().GetHandleTable(); + + /* Get the client port. */ + KScopedAutoObject client_port = handle_table.GetObject(port); + R_UNLESS(client_port.IsNotNull(), svc::ResultInvalidHandle()); + + /* 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 and register session. */ + if (client_port->IsLight()) { + KLightClientSession *session; + R_TRY(client_port->CreateLightSession(std::addressof(session))); + + handle_table.Register(*out, session); + session->Close(); + } else { + KClientSession *session; + R_TRY(client_port->CreateSession(std::addressof(session))); + + handle_table.Register(*out, session); + session->Close(); + } + + /* We succeeded. */ + handle_guard.Cancel(); + return ResultSuccess(); + } + } /* ============================= 64 ABI ============================= */ @@ -121,7 +191,7 @@ namespace ams::kern::svc { } 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) { - MESOSPHERE_PANIC("Stubbed SvcCreatePort64 was called."); + return CreatePort(out_server_handle, out_client_handle, max_sessions, is_light, name); } Result ManageNamedPort64(ams::svc::Handle *out_server_handle, KUserPointer name, int32_t max_sessions) { @@ -129,7 +199,7 @@ namespace ams::kern::svc { } Result ConnectToPort64(ams::svc::Handle *out_handle, ams::svc::Handle port) { - MESOSPHERE_PANIC("Stubbed SvcConnectToPort64 was called."); + return ConnectToPort(out_handle, port); } /* ============================= 64From32 ABI ============================= */ @@ -139,7 +209,7 @@ namespace ams::kern::svc { } 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) { - MESOSPHERE_PANIC("Stubbed SvcCreatePort64From32 was called."); + return CreatePort(out_server_handle, out_client_handle, max_sessions, is_light, name); } Result ManageNamedPort64From32(ams::svc::Handle *out_server_handle, KUserPointer name, int32_t max_sessions) { @@ -147,7 +217,7 @@ namespace ams::kern::svc { } Result ConnectToPort64From32(ams::svc::Handle *out_handle, ams::svc::Handle port) { - MESOSPHERE_PANIC("Stubbed SvcConnectToPort64From32 was called."); + return ConnectToPort(out_handle, port); } }