diff options
-rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/core/hle/ipc_helpers.h | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/hle_ipc.cpp | 10 | ||||
-rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 10 | ||||
-rw-r--r-- | src/core/hle/kernel/init/init_slab_setup.cpp | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/k_client_port.cpp | 119 | ||||
-rw-r--r-- | src/core/hle/kernel/k_client_port.h | 46 | ||||
-rw-r--r-- | src/core/hle/kernel/k_event.h | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/k_port.cpp | 68 | ||||
-rw-r--r-- | src/core/hle/kernel/k_port.h | 87 | ||||
-rw-r--r-- | src/core/hle/kernel/k_server_port.cpp | 94 | ||||
-rw-r--r-- | src/core/hle/kernel/k_server_port.h | 55 | ||||
-rw-r--r-- | src/core/hle/kernel/k_server_session.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/k_server_session.h | 5 | ||||
-rw-r--r-- | src/core/hle/kernel/k_session.cpp | 18 | ||||
-rw-r--r-- | src/core/hle/kernel/k_session.h | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.h | 6 | ||||
-rw-r--r-- | src/core/hle/kernel/svc.cpp | 22 | ||||
-rw-r--r-- | src/core/hle/kernel/svc_results.h | 1 | ||||
-rw-r--r-- | src/core/hle/service/service.cpp | 9 | ||||
-rw-r--r-- | src/core/hle/service/sm/sm.cpp | 31 | ||||
-rw-r--r-- | src/core/hle/service/sm/sm.h | 13 |
22 files changed, 444 insertions, 166 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 487e6f720..cee6d30f6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -194,6 +194,8 @@ add_library(core STATIC hle/kernel/k_page_linked_list.h hle/kernel/k_page_table.cpp hle/kernel/k_page_table.h + hle/kernel/k_port.cpp + hle/kernel/k_port.h hle/kernel/k_priority_queue.h hle/kernel/k_readable_event.cpp hle/kernel/k_readable_event.h diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 18aebf6ea..8128445fd 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -136,10 +136,10 @@ public: context->AddDomainObject(std::move(iface)); } else { auto* session = Kernel::KSession::Create(kernel); - session->Initialize(iface->GetServiceName()); + session->Initialize(nullptr, iface->GetServiceName()); context->AddMoveObject(&session->GetClientSession()); - iface->ClientConnected(session); + iface->ClientConnected(&session->GetServerSession()); } } diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 9e1e63204..ddff9ce99 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -35,14 +35,12 @@ SessionRequestHandler::SessionRequestHandler() = default; SessionRequestHandler::~SessionRequestHandler() = default; -void SessionRequestHandler::ClientConnected(KSession* session) { - session->GetServerSession().SetHleHandler(shared_from_this()); - sessions.push_back(session); +void SessionRequestHandler::ClientConnected(KServerSession* session) { + session->SetHleHandler(shared_from_this()); } -void SessionRequestHandler::ClientDisconnected(KSession* session) { - session->GetServerSession().SetHleHandler(nullptr); - boost::range::remove_erase(sessions, session); +void SessionRequestHandler::ClientDisconnected(KServerSession* session) { + session->SetHleHandler(nullptr); } HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index b7484c445..d63c730ac 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -72,20 +72,14 @@ public: * associated ServerSession alive for the duration of the connection. * @param server_session Owning pointer to the ServerSession associated with the connection. */ - void ClientConnected(KSession* session); + void ClientConnected(KServerSession* session); /** * Signals that a client has just disconnected from this HLE handler and releases the * associated ServerSession. * @param server_session ServerSession associated with the connection. */ - void ClientDisconnected(KSession* session); - -protected: - /// List of sessions that are connected to this handler. - /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list - /// for the duration of the connection. - std::vector<KSession*> sessions; + void ClientDisconnected(KServerSession* session); }; /** diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index a5ddd7344..f8c255732 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -12,6 +12,7 @@ #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_memory_layout.h" #include "core/hle/kernel/k_memory_manager.h" +#include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_shared_memory.h" @@ -30,8 +31,9 @@ namespace Kernel::Init { HANDLER(Process, (SLAB_COUNT(Process)), ##__VA_ARGS__) \ HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \ HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ - HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ + HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ + HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index 00e1bbc59..b6f1d713f 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -1,12 +1,14 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2021 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/scope_exit.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_client_port.h" -#include "core/hle/kernel/k_server_port.h" +#include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_session.h" -#include "core/hle/kernel/object.h" #include "core/hle/kernel/svc_results.h" namespace Kernel { @@ -14,45 +16,110 @@ namespace Kernel { KClientPort::KClientPort(KernelCore& kernel) : KSynchronizationObject{kernel} {} KClientPort::~KClientPort() = default; -void KClientPort::Initialize(s32 max_sessions_, std::string&& name_) { +void KClientPort::Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_) { + // Set member variables. + num_sessions = 0; + peak_sessions = 0; + parent = parent_; max_sessions = max_sessions_; name = std::move(name_); } -KServerPort* KClientPort::GetServerPort() const { - return server_port; -} +void KClientPort::OnSessionFinalized() { + KScopedSchedulerLock sl{kernel}; -ResultVal<KClientSession*> KClientPort::Connect() { - if (num_sessions >= max_sessions) { - return ResultOutOfSessions; + const auto prev = num_sessions--; + if (prev == max_sessions) { + this->NotifyAvailable(); } - num_sessions++; +} - auto* session = Kernel::KSession::Create(kernel); - session->Initialize(name + ":ClientPort"); +void KClientPort::OnServerClosed() {} - if (server_port->HasHLEHandler()) { - server_port->GetHLEHandler()->ClientConnected(session); - } else { - server_port->AppendPendingSession(std::addressof(session->GetServerSession())); - } +bool KClientPort::IsLight() const { + return this->GetParent()->IsLight(); +} - return MakeResult(std::addressof(session->GetClientSession())); +bool KClientPort::IsServerClosed() const { + return this->GetParent()->IsServerClosed(); } -void KClientPort::ConnectionClosed() { - if (num_sessions == 0) { - return; - } +void KClientPort::Destroy() { + // Note with our parent that we're closed. + parent->OnClientClosed(); - --num_sessions; + // Close our reference to our parent. + parent->Close(); } -void KClientPort::Destroy() {} - bool KClientPort::IsSignaled() const { return num_sessions < max_sessions; } +ResultCode KClientPort::CreateSession(KClientSession** out) { + // Reserve a new session from the resource limit. + KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), + LimitableResource::Sessions); + R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); + + // Update the session counts. + { + // Atomically increment the number of sessions. + s32 new_sessions; + { + const auto max = max_sessions; + auto cur_sessions = num_sessions.load(std::memory_order_acquire); + do { + R_UNLESS(cur_sessions < max, ResultOutOfSessions); + new_sessions = cur_sessions + 1; + } while (!num_sessions.compare_exchange_weak(cur_sessions, new_sessions, + std::memory_order_relaxed)); + } + + // Atomically update the peak session tracking. + { + auto peak = peak_sessions.load(std::memory_order_acquire); + do { + if (peak >= new_sessions) { + break; + } + } while (!peak_sessions.compare_exchange_weak(peak, new_sessions, + std::memory_order_relaxed)); + } + } + + // Create a new session. + KSession* session = KSession::Create(kernel); + if (session == nullptr) { + /* Decrement the session count. */ + const auto prev = num_sessions--; + if (prev == max_sessions) { + this->NotifyAvailable(); + } + + return ResultOutOfResource; + } + + // Initialize the session. + session->Initialize(this, parent->GetName()); + + // Commit the session reservation. + session_reservation.Commit(); + + // Register the session. + KSession::Register(kernel, session); + auto session_guard = SCOPE_GUARD({ + session->GetClientSession().Close(); + session->GetServerSession().Close(); + }); + + // Enqueue the session with our parent. + R_TRY(parent->EnqueueSession(std::addressof(session->GetServerSession()))); + + // We succeeded, so set the output. + session_guard.Cancel(); + *out = std::addressof(session->GetClientSession()); + return RESULT_SUCCESS; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h index 60dea4763..43a17f4a4 100644 --- a/src/core/hle/kernel/k_client_port.h +++ b/src/core/hle/kernel/k_client_port.h @@ -15,7 +15,7 @@ namespace Kernel { class KClientSession; class KernelCore; -class KServerPort; +class KPort; class KClientPort final : public KSynchronizationObject { KERNEL_AUTOOBJECT_TRAITS(KClientPort, KSynchronizationObject); @@ -24,30 +24,33 @@ public: explicit KClientPort(KernelCore& kernel); virtual ~KClientPort() override; - friend class KServerPort; + void Initialize(KPort* parent_, s32 max_sessions_, std::string&& name_); + void OnSessionFinalized(); + void OnServerClosed(); - void Initialize(s32 max_sessions_, std::string&& name_); - - KServerPort* GetServerPort() const; + constexpr const KPort* GetParent() const { + return parent; + } - /** - * Creates a new Session pair, adds the created ServerSession to the associated ServerPort's - * list of pending sessions, and signals the ServerPort, causing any threads - * waiting on it to awake. - * @returns ClientSession The client endpoint of the created Session pair, or error code. - */ - ResultVal<KClientSession*> Connect(); + s32 GetNumSessions() const { + return num_sessions; + } + s32 GetPeakSessions() const { + return peak_sessions; + } + s32 GetMaxSessions() const { + return max_sessions; + } - /** - * Signifies that a previously active connection has been closed, - * decreasing the total number of active connections to this port. - */ - void ConnectionClosed(); + bool IsLight() const; + bool IsServerClosed() const; // Overridden virtual functions. virtual void Destroy() override; virtual bool IsSignaled() const override; + ResultCode CreateSession(KClientSession** out); + // DEPRECATED std::string GetTypeName() const override { @@ -63,10 +66,11 @@ public: } private: - KServerPort* server_port{}; ///< ServerPort associated with this client port. - s32 max_sessions{}; ///< Maximum number of simultaneous sessions the port can have - std::atomic<s32> num_sessions{}; ///< Number of currently open sessions to this port - std::string name; ///< Name of client port (optional) + std::atomic<s32> num_sessions{}; + std::atomic<s32> peak_sessions{}; + s32 max_sessions{}; + KPort* parent{}; + std::string name; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h index 2c48a0499..45634e401 100644 --- a/src/core/hle/kernel/k_event.h +++ b/src/core/hle/kernel/k_event.h @@ -20,7 +20,7 @@ class KEvent final : public KAutoObjectWithSlabHeapAndContainer<KEvent, KAutoObj public: explicit KEvent(KernelCore& kernel); - ~KEvent() override; + virtual ~KEvent(); void Initialize(std::string&& name); diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp new file mode 100644 index 000000000..734aa2a8c --- /dev/null +++ b/src/core/hle/kernel/k_port.cpp @@ -0,0 +1,68 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/svc_results.h" + +namespace Kernel { + +KPort::KPort(KernelCore& kernel) + : KAutoObjectWithSlabHeapAndContainer{kernel}, server{kernel}, client{kernel} {} + +KPort::~KPort() = default; + +void KPort::Initialize(s32 max_sessions_, bool is_light_, const std::string& name_) { + // Open a new reference count to the initialized port. + Open(); + + // Create and initialize our server/client pair. + KAutoObject::Create(std::addressof(server)); + KAutoObject::Create(std::addressof(client)); + server.Initialize(this, name_ + ":Server"); + client.Initialize(this, max_sessions_, name_ + ":Client"); + + // Set our member variables. + is_light = is_light_; + name = name_; + state = State::Normal; +} + +void KPort::OnClientClosed() { + KScopedSchedulerLock sl{kernel}; + + if (state == State::Normal) { + state = State::ClientClosed; + } +} + +void KPort::OnServerClosed() { + KScopedSchedulerLock sl{kernel}; + + if (state == State::Normal) { + state = State::ServerClosed; + } +} + +bool KPort::IsServerClosed() const { + KScopedSchedulerLock sl{kernel}; + return state == State::ServerClosed; +} + +ResultCode KPort::EnqueueSession(KServerSession* session) { + KScopedSchedulerLock sl{kernel}; + + R_UNLESS(state == State::Normal, ResultPortClosed); + + if (server.HasHLEHandler()) { + server.GetHLEHandler()->ClientConnected(session); + } else { + server.EnqueueSession(session); + } + + return RESULT_SUCCESS; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h new file mode 100644 index 000000000..68c8ed8df --- /dev/null +++ b/src/core/hle/kernel/k_port.h @@ -0,0 +1,87 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> + +#include "common/common_types.h" +#include "core/hle/kernel/k_client_port.h" +#include "core/hle/kernel/k_server_port.h" +#include "core/hle/kernel/slab_helpers.h" +#include "core/hle/result.h" + +namespace Kernel { + +class KServerSession; + +class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> { + KERNEL_AUTOOBJECT_TRAITS(KPort, KAutoObject); + +public: + explicit KPort(KernelCore& kernel); + virtual ~KPort(); + + static void PostDestroy([[maybe_unused]] uintptr_t arg) {} + + void Initialize(s32 max_sessions_, bool is_light_, const std::string& name_); + void OnClientClosed(); + void OnServerClosed(); + + bool IsLight() const { + return is_light; + } + + bool IsServerClosed() const; + + ResultCode EnqueueSession(KServerSession* session); + + KClientPort& GetClientPort() { + return client; + } + KServerPort& GetServerPort() { + return server; + } + const KClientPort& GetClientPort() const { + return client; + } + const KServerPort& GetServerPort() const { + return server; + } + + // DEPRECATED + + friend class ServerPort; + std::string GetTypeName() const override { + return "Port"; + } + std::string GetName() const override { + return name; + } + + HandleType GetHandleType() const override { + return {}; + } + + void Finalize() override {} + +private: + enum class State : u8 { + Invalid = 0, + Normal = 1, + ClientClosed = 2, + ServerClosed = 3, + }; + +private: + KServerPort server; + KClientPort client; + State state{State::Invalid}; + bool is_light{}; + + std::string name; ///< Name of client port (optional) +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp index 2c51d66db..fcc04abaa 100644 --- a/src/core/hle/kernel/k_server_port.cpp +++ b/src/core/hle/kernel/k_server_port.cpp @@ -1,10 +1,12 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2021 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include <tuple> #include "common/assert.h" #include "core/hle/kernel/k_client_port.h" +#include "core/hle/kernel/k_port.h" +#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_thread.h" @@ -16,50 +18,88 @@ namespace Kernel { KServerPort::KServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {} KServerPort::~KServerPort() = default; -void KServerPort::Initialize(std::string&& name_) { +void KServerPort::Initialize(KPort* parent_, std::string&& name_) { // Set member variables. + parent = parent_; name = std::move(name_); } -ResultVal<KServerSession*> KServerPort::Accept() { - if (pending_sessions.empty()) { - return ResultNotFound; - } - - auto* session = pending_sessions.back(); - pending_sessions.pop_back(); - return MakeResult(session); +bool KServerPort::IsLight() const { + return this->GetParent()->IsLight(); } -void KServerPort::AppendPendingSession(KServerSession* pending_session) { - pending_sessions.push_back(std::move(pending_session)); - if (pending_sessions.size() == 1) { - NotifyAvailable(); +void KServerPort::CleanupSessions() { + // Ensure our preconditions are met. + if (this->IsLight()) { + UNIMPLEMENTED(); + } + + // Cleanup the session list. + while (true) { + // Get the last session in the list + KServerSession* session = nullptr; + { + KScopedSchedulerLock sl{kernel}; + if (!session_list.empty()) { + session = std::addressof(session_list.front()); + session_list.pop_front(); + } + } + + // Close the session. + if (session != nullptr) { + session->Close(); + } else { + break; + } } } -void KServerPort::Destroy() {} +void KServerPort::Destroy() { + // Note with our parent that we're closed. + parent->OnServerClosed(); + + // Perform necessary cleanup of our session lists. + this->CleanupSessions(); + + // Close our reference to our parent. + parent->Close(); +} bool KServerPort::IsSignaled() const { - return !pending_sessions.empty(); + if (this->IsLight()) { + UNIMPLEMENTED(); + return false; + } else { + return !session_list.empty(); + } } -KServerPort::PortPair KServerPort::CreatePortPair(KernelCore& kernel, u32 max_sessions, - std::string name) { - KServerPort* server_port = new KServerPort(kernel); - KClientPort* client_port = new KClientPort(kernel); +void KServerPort::EnqueueSession(KServerSession* session) { + ASSERT(!this->IsLight()); + + KScopedSchedulerLock sl{kernel}; - KAutoObject::Create(server_port); - KAutoObject::Create(client_port); + // Add the session to our queue. + session_list.push_back(*session); + if (session_list.size() == 1) { + this->NotifyAvailable(); + } +} - server_port->Initialize(name + "_Server"); - client_port->Initialize(max_sessions, name + "_Client"); +KServerSession* KServerPort::AcceptSession() { + ASSERT(!this->IsLight()); - client_port->server_port = server_port; + KScopedSchedulerLock sl{kernel}; - server_port->name = name + "_Server"; + // Return the first session in the list. + if (session_list.empty()) { + return nullptr; + } - return std::make_pair(server_port, client_port); + KServerSession* session = std::addressof(session_list.front()); + session_list.pop_front(); + return session; } } // namespace Kernel diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h index 13fa54e5e..9f45ca3f4 100644 --- a/src/core/hle/kernel/k_server_port.h +++ b/src/core/hle/kernel/k_server_port.h @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2021 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -8,46 +8,33 @@ #include <string> #include <utility> #include <vector> + +#include <boost/intrusive/list.hpp> + #include "common/common_types.h" +#include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_synchronization_object.h" -#include "core/hle/kernel/object.h" #include "core/hle/result.h" namespace Kernel { -class KClientPort; class KernelCore; -class KServerSession; +class KPort; class SessionRequestHandler; class KServerPort final : public KSynchronizationObject { KERNEL_AUTOOBJECT_TRAITS(KServerPort, KSynchronizationObject); +private: + using SessionList = boost::intrusive::list<KServerSession>; + public: explicit KServerPort(KernelCore& kernel); virtual ~KServerPort() override; using HLEHandler = std::shared_ptr<SessionRequestHandler>; - using PortPair = std::pair<KServerPort*, KClientPort*>; - - void Initialize(std::string&& name_); - - /** - * Creates a pair of ServerPort and an associated ClientPort. - * - * @param kernel The kernel instance to create the port pair under. - * @param max_sessions Maximum number of sessions to the port - * @param name Optional name of the ports - * @return The created port tuple - */ - static PortPair CreatePortPair(KernelCore& kernel, u32 max_sessions, - std::string name = "UnknownPort"); - /** - * Accepts a pending incoming connection on this port. If there are no pending sessions, will - * return ERR_NO_PENDING_SESSIONS. - */ - ResultVal<KServerSession*> Accept(); + void Initialize(KPort* parent_, std::string&& name_); /// Whether or not this server port has an HLE handler available. bool HasHLEHandler() const { @@ -67,9 +54,15 @@ public: hle_handler = std::move(hle_handler_); } - /// Appends a ServerSession to the collection of ServerSessions - /// waiting to be accepted by this port. - void AppendPendingSession(KServerSession* pending_session); + void EnqueueSession(KServerSession* pending_session); + + KServerSession* AcceptSession(); + + constexpr const KPort* GetParent() const { + return parent; + } + + bool IsLight() const; // Overridden virtual functions. virtual void Destroy() override; @@ -90,14 +83,12 @@ public: } private: - /// ServerSessions waiting to be accepted by the port - std::vector<KServerSession*> pending_sessions; + void CleanupSessions(); - /// This session's HLE request handler template (optional) - /// ServerSessions created from this port inherit a reference to this handler. +private: + SessionList session_list; HLEHandler hle_handler; - - /// Name of the port (optional) + KPort* parent{}; std::string name; }; diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 46ba7081b..863f9aa5f 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -50,7 +50,7 @@ void KServerSession::OnClientClosed() { if (handler) { // Note that after this returns, this server session's hle_handler is // invalidated (set to null). - handler->ClientDisconnected(parent); + handler->ClientDisconnected(this); } } diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h index ef81c4e30..d748754d0 100644 --- a/src/core/hle/kernel/k_server_session.h +++ b/src/core/hle/kernel/k_server_session.h @@ -9,6 +9,8 @@ #include <utility> #include <vector> +#include <boost/intrusive/list.hpp> + #include "common/threadsafe_queue.h" #include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/service_thread.h" @@ -31,7 +33,8 @@ class KSession; class SessionRequestHandler; class KThread; -class KServerSession final : public KSynchronizationObject { +class KServerSession final : public KSynchronizationObject, + public boost::intrusive::list_base_hook<> { KERNEL_AUTOOBJECT_TRAITS(KServerSession, KSynchronizationObject); friend class ServiceThread; diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index ca1cf18cd..6f4276189 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -1,8 +1,9 @@ -// Copyright 2019 yuzu emulator team +// Copyright 2021 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include "common/assert.h" +#include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_scoped_resource_reservation.h" #include "core/hle/kernel/k_server_session.h" @@ -14,7 +15,7 @@ KSession::KSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel}, server{kernel}, client{kernel} {} KSession::~KSession() = default; -void KSession::Initialize(std::string&& name_) { +void KSession::Initialize(KClientPort* port_, std::string&& name_) { // 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 @@ -37,11 +38,22 @@ void KSession::Initialize(std::string&& name_) { process = kernel.CurrentProcess(); process->Open(); + // Set our port. + port = port_; + if (port != nullptr) { + port->Open(); + } + // Mark initialized. initialized = true; } -void KSession::Finalize() {} +void KSession::Finalize() { + if (port != nullptr) { + port->OnSessionFinalized(); + port->Close(); + } +} void KSession::OnServerClosed() { if (GetState() == State::Normal) { diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h index 6a6fcb588..1597cc608 100644 --- a/src/core/hle/kernel/k_session.h +++ b/src/core/hle/kernel/k_session.h @@ -28,7 +28,7 @@ public: explicit KSession(KernelCore& kernel); virtual ~KSession() override; - void Initialize(std::string&& name_); + void Initialize(KClientPort* port_, std::string&& name_); virtual void Finalize() override; diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 19b3530b4..723be6b51 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -37,6 +37,8 @@ class KClientSession; class KEvent; class KLinkedListNode; class KMemoryManager; +class KPort; +class Process; class KResourceLimit; class KScheduler; class KSession; @@ -45,7 +47,6 @@ class KThread; class KTransferMemory; class KWritableEvent; class PhysicalCore; -class Process; class ServiceThread; class Synchronization; class TimeManager; @@ -272,6 +273,8 @@ public: return slab_heap_container->event; } else if constexpr (std::is_same_v<T, KLinkedListNode>) { return slab_heap_container->linked_list_node; + } else if constexpr (std::is_same_v<T, KPort>) { + return slab_heap_container->port; } else if constexpr (std::is_same_v<T, Process>) { return slab_heap_container->process; } else if constexpr (std::is_same_v<T, KResourceLimit>) { @@ -323,6 +326,7 @@ private: KSlabHeap<KClientSession> client_session; KSlabHeap<KEvent> event; KSlabHeap<KLinkedListNode> linked_list_node; + KSlabHeap<KPort> port; KSlabHeap<Process> process; KSlabHeap<KResourceLimit> resource_limit; KSlabHeap<KSession> session; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 8d8d3dd5a..ef8fa98a9 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -293,9 +293,7 @@ static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr /// Connect to an OS service given the port name, returns the handle to the port to out static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, VAddr port_name_address) { - std::lock_guard lock{HLE::g_hle_lock}; auto& memory = system.Memory(); - if (!memory.IsValidVirtualAddress(port_name_address)) { LOG_ERROR(Kernel_SVC, "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}", @@ -314,21 +312,27 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); + // Get the current handle table. auto& kernel = system.Kernel(); + auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + + // Find the client port. const auto it = kernel.FindNamedPort(port_name); if (!kernel.IsValidNamedPort(it)) { LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name); return ResultNotFound; } + auto port = it->second; - auto client_port = it->second; + // Create a session. + KClientSession* session{}; + R_TRY(port->CreateSession(std::addressof(session))); - KClientSession* client_session{}; - CASCADE_RESULT(client_session, client_port->Connect()); + // Register the session in the table, close the extra reference. + handle_table.Add(out_handle, session); + session->Close(); - // Return the client session - auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); - handle_table.Add(out_handle, client_session); + // We succeeded. return RESULT_SUCCESS; } @@ -340,13 +344,13 @@ static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle, /// Makes a blocking IPC call to an OS service. static ResultCode SendSyncRequest(Core::System& system, Handle handle) { - LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); auto& kernel = system.Kernel(); KScopedAutoObject session = kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle); R_UNLESS(session.IsNotNull(), ResultInvalidHandle); + LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); auto thread = kernel.CurrentScheduler()->GetCurrentThread(); { diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h index cd32acd99..53a940723 100644 --- a/src/core/hle/kernel/svc_results.h +++ b/src/core/hle/kernel/svc_results.h @@ -36,6 +36,7 @@ constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122}; constexpr ResultCode ResultSessionClosed{ErrorModule::Kernel, 123}; constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; constexpr ResultCode ResultReservedUsed{ErrorModule::Kernel, 126}; +constexpr ResultCode ResultPortClosed{ErrorModule::Kernel, 131}; constexpr ResultCode ResultLimitReached{ErrorModule::Kernel, 132}; constexpr ResultCode ResultInvalidId{ErrorModule::Kernel, 519}; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index a882b3b4e..42e464024 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -116,10 +116,11 @@ void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) { ASSERT(!port_installed); - auto [server_port, client_port] = - Kernel::KServerPort::CreatePortPair(kernel, max_sessions, service_name); - server_port->SetHleHandler(shared_from_this()); - kernel.AddNamedPort(service_name, client_port); + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(max_sessions, false, service_name); + port->GetServerPort().SetHleHandler(shared_from_this()); + kernel.AddNamedPort(service_name, &port->GetClientPort()); + port_installed = true; } diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 51274bfb1..71ab4b6f5 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -8,6 +8,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_client_port.h" #include "core/hle/kernel/k_client_session.h" +#include "core/hle/kernel/k_port.h" #include "core/hle/kernel/k_server_port.h" #include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_session.h" @@ -59,13 +60,12 @@ ResultVal<Kernel::KServerPort*> ServiceManager::RegisterService(std::string name return ERR_ALREADY_REGISTERED; } - auto [server_port, client_port] = - Kernel::KServerPort::CreatePortPair(kernel, max_sessions, name); + auto* port = Kernel::KPort::Create(kernel); + port->Initialize(max_sessions, false, name); - client_port->Open(); + registered_services.emplace(std::move(name), port); - registered_services.emplace(std::move(name), client_port); - return MakeResult(server_port); + return MakeResult(&port->GetServerPort()); } ResultCode ServiceManager::UnregisterService(const std::string& name) { @@ -83,7 +83,7 @@ ResultCode ServiceManager::UnregisterService(const std::string& name) { return RESULT_SUCCESS; } -ResultVal<Kernel::KClientPort*> ServiceManager::GetServicePort(const std::string& name) { +ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) { CASCADE_CODE(ValidateServiceName(name)); auto it = registered_services.find(name); @@ -118,25 +118,26 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { std::string name(name_buf.begin(), end); - auto client_port = service_manager->GetServicePort(name); - if (client_port.Failed()) { + auto result = service_manager->GetServicePort(name); + if (result.Failed()) { IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(client_port.Code()); - LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw); + rb.Push(result.Code()); + LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.Code().raw); if (name.length() == 0) return; // LibNX Fix UNIMPLEMENTED(); return; } + auto* port = result.Unwrap(); + auto* session = Kernel::KSession::Create(kernel); - session->Initialize(std::move(name)); + session->Initialize(&port->GetClientPort(), std::move(name)); - const auto& server_port = client_port.Unwrap()->GetServerPort(); - if (server_port->GetHLEHandler()) { - server_port->GetHLEHandler()->ClientConnected(session); + if (port->GetServerPort().GetHLEHandler()) { + port->GetServerPort().GetHLEHandler()->ClientConnected(&session->GetServerSession()); } else { - server_port->AppendPendingSession(&session->GetServerSession()); + port->EnqueueSession(&session->GetServerSession()); } LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetObjectId()); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index b0204c4bb..af5010c3b 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -10,9 +10,7 @@ #include <unordered_map> #include "common/concepts.h" -#include "core/hle/kernel/k_client_port.h" -#include "core/hle/kernel/k_server_port.h" -#include "core/hle/kernel/object.h" +#include "core/hle/kernel/k_port.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -24,6 +22,7 @@ namespace Kernel { class KClientPort; class KClientSession; class KernelCore; +class KPort; class KServerPort; class SessionRequestHandler; } // namespace Kernel @@ -57,7 +56,7 @@ public: ResultVal<Kernel::KServerPort*> RegisterService(std::string name, u32 max_sessions); ResultCode UnregisterService(const std::string& name); - ResultVal<Kernel::KClientPort*> GetServicePort(const std::string& name); + ResultVal<Kernel::KPort*> GetServicePort(const std::string& name); template <Common::DerivedFrom<Kernel::SessionRequestHandler> T> std::shared_ptr<T> GetService(const std::string& service_name) const { @@ -66,11 +65,11 @@ public: LOG_DEBUG(Service, "Can't find service: {}", service_name); return nullptr; } - auto port = service->second->GetServerPort(); + auto* port = service->second; if (port == nullptr) { return nullptr; } - return std::static_pointer_cast<T>(port->GetHLEHandler()); + return std::static_pointer_cast<T>(port->GetServerPort().GetHLEHandler()); } void InvokeControlRequest(Kernel::HLERequestContext& context); @@ -80,7 +79,7 @@ private: std::unique_ptr<Controller> controller_interface; /// Map of registered services, retrieved using GetServicePort. - std::unordered_map<std::string, Kernel::KClientPort*> registered_services; + std::unordered_map<std::string, Kernel::KPort*> registered_services; /// Kernel context Kernel::KernelCore& kernel; |