mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	Merge pull request #9135 from liamwhite/service-thread-event
kernel: invert session request handling flow
This commit is contained in:
		@@ -86,13 +86,13 @@ public:
 | 
				
			|||||||
        u32 num_domain_objects{};
 | 
					        u32 num_domain_objects{};
 | 
				
			||||||
        const bool always_move_handles{
 | 
					        const bool always_move_handles{
 | 
				
			||||||
            (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
 | 
					            (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
 | 
				
			||||||
        if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) {
 | 
					        if (!ctx.GetManager()->IsDomain() || always_move_handles) {
 | 
				
			||||||
            num_handles_to_move = num_objects_to_move;
 | 
					            num_handles_to_move = num_objects_to_move;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            num_domain_objects = num_objects_to_move;
 | 
					            num_domain_objects = num_objects_to_move;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (ctx.Session()->GetSessionRequestManager()->IsDomain()) {
 | 
					        if (ctx.GetManager()->IsDomain()) {
 | 
				
			||||||
            raw_data_size +=
 | 
					            raw_data_size +=
 | 
				
			||||||
                static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
 | 
					                static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
 | 
				
			||||||
            ctx.write_size += num_domain_objects;
 | 
					            ctx.write_size += num_domain_objects;
 | 
				
			||||||
@@ -125,8 +125,7 @@ public:
 | 
				
			|||||||
        if (!ctx.IsTipc()) {
 | 
					        if (!ctx.IsTipc()) {
 | 
				
			||||||
            AlignWithPadding();
 | 
					            AlignWithPadding();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (ctx.Session()->GetSessionRequestManager()->IsDomain() &&
 | 
					            if (ctx.GetManager()->IsDomain() && ctx.HasDomainMessageHeader()) {
 | 
				
			||||||
                ctx.HasDomainMessageHeader()) {
 | 
					 | 
				
			||||||
                IPC::DomainMessageHeader domain_header{};
 | 
					                IPC::DomainMessageHeader domain_header{};
 | 
				
			||||||
                domain_header.num_objects = num_domain_objects;
 | 
					                domain_header.num_objects = num_domain_objects;
 | 
				
			||||||
                PushRaw(domain_header);
 | 
					                PushRaw(domain_header);
 | 
				
			||||||
@@ -146,18 +145,18 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    template <class T>
 | 
					    template <class T>
 | 
				
			||||||
    void PushIpcInterface(std::shared_ptr<T> iface) {
 | 
					    void PushIpcInterface(std::shared_ptr<T> iface) {
 | 
				
			||||||
        if (context->Session()->GetSessionRequestManager()->IsDomain()) {
 | 
					        if (context->GetManager()->IsDomain()) {
 | 
				
			||||||
            context->AddDomainObject(std::move(iface));
 | 
					            context->AddDomainObject(std::move(iface));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            kernel.CurrentProcess()->GetResourceLimit()->Reserve(
 | 
					            kernel.CurrentProcess()->GetResourceLimit()->Reserve(
 | 
				
			||||||
                Kernel::LimitableResource::Sessions, 1);
 | 
					                Kernel::LimitableResource::Sessions, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            auto* session = Kernel::KSession::Create(kernel);
 | 
					            auto* session = Kernel::KSession::Create(kernel);
 | 
				
			||||||
            session->Initialize(nullptr, iface->GetServiceName(),
 | 
					            session->Initialize(nullptr, iface->GetServiceName());
 | 
				
			||||||
                                std::make_shared<Kernel::SessionRequestManager>(kernel));
 | 
					            iface->RegisterSession(&session->GetServerSession(),
 | 
				
			||||||
 | 
					                                   std::make_shared<Kernel::SessionRequestManager>(kernel));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            context->AddMoveObject(&session->GetClientSession());
 | 
					            context->AddMoveObject(&session->GetClientSession());
 | 
				
			||||||
            iface->ClientConnected(&session->GetServerSession());
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -387,7 +386,7 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    template <class T>
 | 
					    template <class T>
 | 
				
			||||||
    std::weak_ptr<T> PopIpcInterface() {
 | 
					    std::weak_ptr<T> PopIpcInterface() {
 | 
				
			||||||
        ASSERT(context->Session()->GetSessionRequestManager()->IsDomain());
 | 
					        ASSERT(context->GetManager()->IsDomain());
 | 
				
			||||||
        ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
 | 
					        ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
 | 
				
			||||||
        return context->GetDomainHandler<T>(Pop<u32>() - 1);
 | 
					        return context->GetDomainHandler<T>(Pop<u32>() - 1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@
 | 
				
			|||||||
#include "core/hle/kernel/k_auto_object.h"
 | 
					#include "core/hle/kernel/k_auto_object.h"
 | 
				
			||||||
#include "core/hle/kernel/k_handle_table.h"
 | 
					#include "core/hle/kernel/k_handle_table.h"
 | 
				
			||||||
#include "core/hle/kernel/k_process.h"
 | 
					#include "core/hle/kernel/k_process.h"
 | 
				
			||||||
 | 
					#include "core/hle/kernel/k_server_port.h"
 | 
				
			||||||
#include "core/hle/kernel/k_server_session.h"
 | 
					#include "core/hle/kernel/k_server_session.h"
 | 
				
			||||||
#include "core/hle/kernel/k_thread.h"
 | 
					#include "core/hle/kernel/k_thread.h"
 | 
				
			||||||
#include "core/hle/kernel/kernel.h"
 | 
					#include "core/hle/kernel/kernel.h"
 | 
				
			||||||
@@ -35,7 +36,21 @@ SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* se
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SessionRequestHandler::~SessionRequestHandler() {
 | 
					SessionRequestHandler::~SessionRequestHandler() {
 | 
				
			||||||
    kernel.ReleaseServiceThread(service_thread);
 | 
					    kernel.ReleaseServiceThread(service_thread.lock());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
 | 
				
			||||||
 | 
					    auto* server_session = server_port->AcceptSession();
 | 
				
			||||||
 | 
					    ASSERT(server_session != nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RegisterSession(server_session, std::make_shared<SessionRequestManager>(kernel));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SessionRequestHandler::RegisterSession(KServerSession* server_session,
 | 
				
			||||||
 | 
					                                            std::shared_ptr<SessionRequestManager> manager) {
 | 
				
			||||||
 | 
					    manager->SetSessionHandler(shared_from_this());
 | 
				
			||||||
 | 
					    service_thread.lock()->RegisterServerSession(server_session, manager);
 | 
				
			||||||
 | 
					    server_session->Close();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
 | 
					SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
 | 
				
			||||||
@@ -92,7 +107,7 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
 | 
					    // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
 | 
				
			||||||
    context.SetSessionRequestManager(server_session->GetSessionRequestManager());
 | 
					    ASSERT(context.GetManager().get() == this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // If there is a DomainMessageHeader, then this is CommandType "Request"
 | 
					    // If there is a DomainMessageHeader, then this is CommandType "Request"
 | 
				
			||||||
    const auto& domain_message_header = context.GetDomainMessageHeader();
 | 
					    const auto& domain_message_header = context.GetDomainMessageHeader();
 | 
				
			||||||
@@ -130,31 +145,6 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses
 | 
				
			|||||||
    return ResultSuccess;
 | 
					    return ResultSuccess;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Result SessionRequestManager::QueueSyncRequest(KSession* parent,
 | 
					 | 
				
			||||||
                                               std::shared_ptr<HLERequestContext>&& context) {
 | 
					 | 
				
			||||||
    // Ensure we have a session request handler
 | 
					 | 
				
			||||||
    if (this->HasSessionRequestHandler(*context)) {
 | 
					 | 
				
			||||||
        if (auto strong_ptr = this->GetServiceThread().lock()) {
 | 
					 | 
				
			||||||
            strong_ptr->QueueSyncRequest(*parent, std::move(context));
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            ASSERT_MSG(false, "strong_ptr is nullptr!");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        ASSERT_MSG(false, "handler is invalid!");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ResultSuccess;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SessionRequestHandler::ClientConnected(KServerSession* session) {
 | 
					 | 
				
			||||||
    session->GetSessionRequestManager()->SetSessionHandler(shared_from_this());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ensure our server session is tracked globally.
 | 
					 | 
				
			||||||
    kernel.RegisterServerObject(session);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
 | 
					HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
 | 
				
			||||||
                                     KServerSession* server_session_, KThread* thread_)
 | 
					                                     KServerSession* server_session_, KThread* thread_)
 | 
				
			||||||
    : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
 | 
					    : server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
 | 
				
			||||||
@@ -214,7 +204,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
 | 
				
			|||||||
        // Padding to align to 16 bytes
 | 
					        // Padding to align to 16 bytes
 | 
				
			||||||
        rp.AlignWithPadding();
 | 
					        rp.AlignWithPadding();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (Session()->GetSessionRequestManager()->IsDomain() &&
 | 
					        if (GetManager()->IsDomain() &&
 | 
				
			||||||
            ((command_header->type == IPC::CommandType::Request ||
 | 
					            ((command_header->type == IPC::CommandType::Request ||
 | 
				
			||||||
              command_header->type == IPC::CommandType::RequestWithContext) ||
 | 
					              command_header->type == IPC::CommandType::RequestWithContext) ||
 | 
				
			||||||
             !incoming)) {
 | 
					             !incoming)) {
 | 
				
			||||||
@@ -223,7 +213,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
 | 
				
			|||||||
            if (incoming || domain_message_header) {
 | 
					            if (incoming || domain_message_header) {
 | 
				
			||||||
                domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
 | 
					                domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                if (Session()->GetSessionRequestManager()->IsDomain()) {
 | 
					                if (GetManager()->IsDomain()) {
 | 
				
			||||||
                    LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
 | 
					                    LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -316,12 +306,11 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
 | 
				
			|||||||
    // Write the domain objects to the command buffer, these go after the raw untranslated data.
 | 
					    // Write the domain objects to the command buffer, these go after the raw untranslated data.
 | 
				
			||||||
    // TODO(Subv): This completely ignores C buffers.
 | 
					    // TODO(Subv): This completely ignores C buffers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (server_session->GetSessionRequestManager()->IsDomain()) {
 | 
					    if (GetManager()->IsDomain()) {
 | 
				
			||||||
        current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
 | 
					        current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
 | 
				
			||||||
        for (auto& object : outgoing_domain_objects) {
 | 
					        for (auto& object : outgoing_domain_objects) {
 | 
				
			||||||
            server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object));
 | 
					            GetManager()->AppendDomainHandler(std::move(object));
 | 
				
			||||||
            cmd_buf[current_offset++] = static_cast<u32_le>(
 | 
					            cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
 | 
				
			||||||
                server_session->GetSessionRequestManager()->DomainHandlerCount());
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,11 +45,13 @@ class KAutoObject;
 | 
				
			|||||||
class KernelCore;
 | 
					class KernelCore;
 | 
				
			||||||
class KEvent;
 | 
					class KEvent;
 | 
				
			||||||
class KHandleTable;
 | 
					class KHandleTable;
 | 
				
			||||||
 | 
					class KServerPort;
 | 
				
			||||||
class KProcess;
 | 
					class KProcess;
 | 
				
			||||||
class KServerSession;
 | 
					class KServerSession;
 | 
				
			||||||
class KThread;
 | 
					class KThread;
 | 
				
			||||||
class KReadableEvent;
 | 
					class KReadableEvent;
 | 
				
			||||||
class KSession;
 | 
					class KSession;
 | 
				
			||||||
 | 
					class SessionRequestManager;
 | 
				
			||||||
class ServiceThread;
 | 
					class ServiceThread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class ThreadWakeupReason;
 | 
					enum class ThreadWakeupReason;
 | 
				
			||||||
@@ -76,19 +78,9 @@ public:
 | 
				
			|||||||
    virtual Result HandleSyncRequest(Kernel::KServerSession& session,
 | 
					    virtual Result HandleSyncRequest(Kernel::KServerSession& session,
 | 
				
			||||||
                                     Kernel::HLERequestContext& context) = 0;
 | 
					                                     Kernel::HLERequestContext& context) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    void AcceptSession(KServerPort* server_port);
 | 
				
			||||||
     * Signals that a client has just connected to this HLE handler and keeps the
 | 
					    void RegisterSession(KServerSession* server_session,
 | 
				
			||||||
     * associated ServerSession alive for the duration of the connection.
 | 
					                         std::shared_ptr<SessionRequestManager> manager);
 | 
				
			||||||
     * @param server_session Owning pointer to the ServerSession associated with the connection.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    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(KServerSession* session);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::weak_ptr<ServiceThread> GetServiceThread() const {
 | 
					    std::weak_ptr<ServiceThread> GetServiceThread() const {
 | 
				
			||||||
        return service_thread;
 | 
					        return service_thread;
 | 
				
			||||||
@@ -170,7 +162,6 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
 | 
					    Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
 | 
				
			||||||
    Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
 | 
					    Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
 | 
				
			||||||
    Result QueueSyncRequest(KSession* parent, std::shared_ptr<HLERequestContext>&& context);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    bool convert_to_domain{};
 | 
					    bool convert_to_domain{};
 | 
				
			||||||
@@ -350,11 +341,11 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    template <typename T>
 | 
					    template <typename T>
 | 
				
			||||||
    std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
 | 
					    std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
 | 
				
			||||||
        return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock());
 | 
					        return std::static_pointer_cast<T>(GetManager()->DomainHandler(index).lock());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
 | 
					    void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
 | 
				
			||||||
        manager = std::move(manager_);
 | 
					        manager = manager_;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::string Description() const;
 | 
					    std::string Description() const;
 | 
				
			||||||
@@ -363,6 +354,10 @@ public:
 | 
				
			|||||||
        return *thread;
 | 
					        return *thread;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_ptr<SessionRequestManager> GetManager() const {
 | 
				
			||||||
 | 
					        return manager.lock();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    friend class IPC::ResponseBuilder;
 | 
					    friend class IPC::ResponseBuilder;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -396,7 +391,7 @@ private:
 | 
				
			|||||||
    u32 handles_offset{};
 | 
					    u32 handles_offset{};
 | 
				
			||||||
    u32 domain_offset{};
 | 
					    u32 domain_offset{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::weak_ptr<SessionRequestManager> manager;
 | 
					    std::weak_ptr<SessionRequestManager> manager{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    KernelCore& kernel;
 | 
					    KernelCore& kernel;
 | 
				
			||||||
    Core::Memory::Memory& memory;
 | 
					    Core::Memory::Memory& memory;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -58,8 +58,7 @@ bool KClientPort::IsSignaled() const {
 | 
				
			|||||||
    return num_sessions < max_sessions;
 | 
					    return num_sessions < max_sessions;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Result KClientPort::CreateSession(KClientSession** out,
 | 
					Result KClientPort::CreateSession(KClientSession** out) {
 | 
				
			||||||
                                  std::shared_ptr<SessionRequestManager> session_manager) {
 | 
					 | 
				
			||||||
    // Reserve a new session from the resource limit.
 | 
					    // Reserve a new session from the resource limit.
 | 
				
			||||||
    KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
 | 
					    KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
 | 
				
			||||||
                                                   LimitableResource::Sessions);
 | 
					                                                   LimitableResource::Sessions);
 | 
				
			||||||
@@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Initialize the session.
 | 
					    // Initialize the session.
 | 
				
			||||||
    session->Initialize(this, parent->GetName(), session_manager);
 | 
					    session->Initialize(this, parent->GetName());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Commit the session reservation.
 | 
					    // Commit the session reservation.
 | 
				
			||||||
    session_reservation.Commit();
 | 
					    session_reservation.Commit();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,8 +52,7 @@ public:
 | 
				
			|||||||
    void Destroy() override;
 | 
					    void Destroy() override;
 | 
				
			||||||
    bool IsSignaled() const override;
 | 
					    bool IsSignaled() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Result CreateSession(KClientSession** out,
 | 
					    Result CreateSession(KClientSession** out);
 | 
				
			||||||
                         std::shared_ptr<SessionRequestManager> session_manager = nullptr);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    std::atomic<s32> num_sessions{};
 | 
					    std::atomic<s32> num_sessions{};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,12 +57,6 @@ Result KPort::EnqueueSession(KServerSession* session) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    server.EnqueueSession(session);
 | 
					    server.EnqueueSession(session);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
 | 
					 | 
				
			||||||
        session_ptr->ClientConnected(server.AcceptSession());
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        ASSERT(false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ResultSuccess;
 | 
					    return ResultSuccess;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -61,12 +61,6 @@ void KServerPort::Destroy() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Close our reference to our parent.
 | 
					    // Close our reference to our parent.
 | 
				
			||||||
    parent->Close();
 | 
					    parent->Close();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Release host emulation members.
 | 
					 | 
				
			||||||
    session_handler.reset();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ensure that the global list tracking server objects does not hold on to a reference.
 | 
					 | 
				
			||||||
    kernel.UnregisterServerObject(this);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool KServerPort::IsSignaled() const {
 | 
					bool KServerPort::IsSignaled() const {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,24 +27,6 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    void Initialize(KPort* parent_port_, std::string&& name_);
 | 
					    void Initialize(KPort* parent_port_, std::string&& name_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Whether or not this server port has an HLE handler available.
 | 
					 | 
				
			||||||
    bool HasSessionRequestHandler() const {
 | 
					 | 
				
			||||||
        return !session_handler.expired();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Gets the HLE handler for this port.
 | 
					 | 
				
			||||||
    SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
 | 
					 | 
				
			||||||
        return session_handler;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
 | 
					 | 
				
			||||||
     * will inherit a reference to this handler.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
 | 
					 | 
				
			||||||
        session_handler = std::move(handler);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    void EnqueueSession(KServerSession* pending_session);
 | 
					    void EnqueueSession(KServerSession* pending_session);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    KServerSession* AcceptSession();
 | 
					    KServerSession* AcceptSession();
 | 
				
			||||||
@@ -65,7 +47,6 @@ private:
 | 
				
			|||||||
    void CleanupSessions();
 | 
					    void CleanupSessions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SessionList session_list;
 | 
					    SessionList session_list;
 | 
				
			||||||
    SessionRequestHandlerWeakPtr session_handler;
 | 
					 | 
				
			||||||
    KPort* parent{};
 | 
					    KPort* parent{};
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
 | 
					// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <tuple>
 | 
					#include <tuple>
 | 
				
			||||||
@@ -33,12 +33,10 @@ KServerSession::KServerSession(KernelCore& kernel_)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
KServerSession::~KServerSession() = default;
 | 
					KServerSession::~KServerSession() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
 | 
					void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) {
 | 
				
			||||||
                                std::shared_ptr<SessionRequestManager> manager_) {
 | 
					 | 
				
			||||||
    // Set member variables.
 | 
					    // Set member variables.
 | 
				
			||||||
    parent = parent_session_;
 | 
					    parent = parent_session_;
 | 
				
			||||||
    name = std::move(name_);
 | 
					    name = std::move(name_);
 | 
				
			||||||
    manager = manager_;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void KServerSession::Destroy() {
 | 
					void KServerSession::Destroy() {
 | 
				
			||||||
@@ -47,18 +45,99 @@ void KServerSession::Destroy() {
 | 
				
			|||||||
    this->CleanupRequests();
 | 
					    this->CleanupRequests();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    parent->Close();
 | 
					    parent->Close();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Release host emulation members.
 | 
					 | 
				
			||||||
    manager.reset();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ensure that the global list tracking server objects does not hold on to a reference.
 | 
					 | 
				
			||||||
    kernel.UnregisterServerObject(this);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void KServerSession::OnClientClosed() {
 | 
					void KServerSession::OnClientClosed() {
 | 
				
			||||||
    if (manager && manager->HasSessionHandler()) {
 | 
					    KScopedLightLock lk{m_lock};
 | 
				
			||||||
        manager->SessionHandler().ClientDisconnected(this);
 | 
					
 | 
				
			||||||
 | 
					    // Handle any pending requests.
 | 
				
			||||||
 | 
					    KSessionRequest* prev_request = nullptr;
 | 
				
			||||||
 | 
					    while (true) {
 | 
				
			||||||
 | 
					        // Declare variables for processing the request.
 | 
				
			||||||
 | 
					        KSessionRequest* request = nullptr;
 | 
				
			||||||
 | 
					        KEvent* event = nullptr;
 | 
				
			||||||
 | 
					        KThread* thread = nullptr;
 | 
				
			||||||
 | 
					        bool cur_request = false;
 | 
				
			||||||
 | 
					        bool terminate = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Get the next request.
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            KScopedSchedulerLock sl{kernel};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (m_current_request != nullptr && m_current_request != prev_request) {
 | 
				
			||||||
 | 
					                // Set the request, open a reference as we process it.
 | 
				
			||||||
 | 
					                request = m_current_request;
 | 
				
			||||||
 | 
					                request->Open();
 | 
				
			||||||
 | 
					                cur_request = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Get thread and event for the request.
 | 
				
			||||||
 | 
					                thread = request->GetThread();
 | 
				
			||||||
 | 
					                event = request->GetEvent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // If the thread is terminating, handle that.
 | 
				
			||||||
 | 
					                if (thread->IsTerminationRequested()) {
 | 
				
			||||||
 | 
					                    request->ClearThread();
 | 
				
			||||||
 | 
					                    request->ClearEvent();
 | 
				
			||||||
 | 
					                    terminate = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                prev_request = request;
 | 
				
			||||||
 | 
					            } else if (!m_request_list.empty()) {
 | 
				
			||||||
 | 
					                // Pop the request from the front of the list.
 | 
				
			||||||
 | 
					                request = std::addressof(m_request_list.front());
 | 
				
			||||||
 | 
					                m_request_list.pop_front();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Get thread and event for the request.
 | 
				
			||||||
 | 
					                thread = request->GetThread();
 | 
				
			||||||
 | 
					                event = request->GetEvent();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If there are no requests, we're done.
 | 
				
			||||||
 | 
					        if (request == nullptr) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // All requests must have threads.
 | 
				
			||||||
 | 
					        ASSERT(thread != nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Ensure that we close the request when done.
 | 
				
			||||||
 | 
					        SCOPE_EXIT({ request->Close(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If we're terminating, close a reference to the thread and event.
 | 
				
			||||||
 | 
					        if (terminate) {
 | 
				
			||||||
 | 
					            thread->Close();
 | 
				
			||||||
 | 
					            if (event != nullptr) {
 | 
				
			||||||
 | 
					                event->Close();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If we need to, reply.
 | 
				
			||||||
 | 
					        if (event != nullptr && !cur_request) {
 | 
				
			||||||
 | 
					            // There must be no mappings.
 | 
				
			||||||
 | 
					            ASSERT(request->GetSendCount() == 0);
 | 
				
			||||||
 | 
					            ASSERT(request->GetReceiveCount() == 0);
 | 
				
			||||||
 | 
					            ASSERT(request->GetExchangeCount() == 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // // Get the process and page table.
 | 
				
			||||||
 | 
					            // KProcess *client_process = thread->GetOwnerProcess();
 | 
				
			||||||
 | 
					            // auto &client_pt = client_process->GetPageTable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // // Reply to the request.
 | 
				
			||||||
 | 
					            // ReplyAsyncError(client_process, request->GetAddress(), request->GetSize(),
 | 
				
			||||||
 | 
					            //                 ResultSessionClosed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // // Unlock the buffer.
 | 
				
			||||||
 | 
					            // // NOTE: Nintendo does not check the result of this.
 | 
				
			||||||
 | 
					            // client_pt.UnlockForIpcUserBuffer(request->GetAddress(), request->GetSize());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Signal the event.
 | 
				
			||||||
 | 
					            event->Signal();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Notify.
 | 
				
			||||||
 | 
					    this->NotifyAvailable(ResultSessionClosed);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool KServerSession::IsSignaled() const {
 | 
					bool KServerSession::IsSignaled() const {
 | 
				
			||||||
@@ -73,24 +152,6 @@ bool KServerSession::IsSignaled() const {
 | 
				
			|||||||
    return !m_request_list.empty() && m_current_request == nullptr;
 | 
					    return !m_request_list.empty() && m_current_request == nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
 | 
					 | 
				
			||||||
    u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
 | 
					 | 
				
			||||||
    auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return manager->QueueSyncRequest(parent, std::move(context));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
 | 
					 | 
				
			||||||
    Result result = manager->CompleteSyncRequest(this, context);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The calling thread is waiting for this request to complete, so wake it up.
 | 
					 | 
				
			||||||
    context.GetThread().EndWait(result);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return result;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Result KServerSession::OnRequest(KSessionRequest* request) {
 | 
					Result KServerSession::OnRequest(KSessionRequest* request) {
 | 
				
			||||||
    // Create the wait queue.
 | 
					    // Create the wait queue.
 | 
				
			||||||
    ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
 | 
					    ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
 | 
				
			||||||
@@ -105,24 +166,16 @@ Result KServerSession::OnRequest(KSessionRequest* request) {
 | 
				
			|||||||
        // Check that we're not terminating.
 | 
					        // Check that we're not terminating.
 | 
				
			||||||
        R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
 | 
					        R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (manager) {
 | 
					        // Get whether we're empty.
 | 
				
			||||||
            // HLE request.
 | 
					        const bool was_empty = m_request_list.empty();
 | 
				
			||||||
            auto& memory{kernel.System().Memory()};
 | 
					 | 
				
			||||||
            this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // Non-HLE request.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Get whether we're empty.
 | 
					        // Add the request to the list.
 | 
				
			||||||
            const bool was_empty = m_request_list.empty();
 | 
					        request->Open();
 | 
				
			||||||
 | 
					        m_request_list.push_back(*request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Add the request to the list.
 | 
					        // If we were empty, signal.
 | 
				
			||||||
            request->Open();
 | 
					        if (was_empty) {
 | 
				
			||||||
            m_request_list.push_back(*request);
 | 
					            this->NotifyAvailable();
 | 
				
			||||||
 | 
					 | 
				
			||||||
            // If we were empty, signal.
 | 
					 | 
				
			||||||
            if (was_empty) {
 | 
					 | 
				
			||||||
                this->NotifyAvailable();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // If we have a request event, this is asynchronous, and we don't need to wait.
 | 
					        // If we have a request event, this is asynchronous, and we don't need to wait.
 | 
				
			||||||
@@ -136,7 +189,7 @@ Result KServerSession::OnRequest(KSessionRequest* request) {
 | 
				
			|||||||
    return GetCurrentThread(kernel).GetWaitResult();
 | 
					    return GetCurrentThread(kernel).GetWaitResult();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Result KServerSession::SendReply() {
 | 
					Result KServerSession::SendReply(bool is_hle) {
 | 
				
			||||||
    // Lock the session.
 | 
					    // Lock the session.
 | 
				
			||||||
    KScopedLightLock lk{m_lock};
 | 
					    KScopedLightLock lk{m_lock};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -171,13 +224,18 @@ Result KServerSession::SendReply() {
 | 
				
			|||||||
    Result result = ResultSuccess;
 | 
					    Result result = ResultSuccess;
 | 
				
			||||||
    if (!closed) {
 | 
					    if (!closed) {
 | 
				
			||||||
        // If we're not closed, send the reply.
 | 
					        // If we're not closed, send the reply.
 | 
				
			||||||
        Core::Memory::Memory& memory{kernel.System().Memory()};
 | 
					        if (is_hle) {
 | 
				
			||||||
        KThread* server_thread{GetCurrentThreadPointer(kernel)};
 | 
					            // HLE servers write directly to a pointer to the thread command buffer. Therefore
 | 
				
			||||||
        UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
 | 
					            // the reply has already been written in this case.
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Core::Memory::Memory& memory{kernel.System().Memory()};
 | 
				
			||||||
 | 
					            KThread* server_thread{GetCurrentThreadPointer(kernel)};
 | 
				
			||||||
 | 
					            UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
 | 
					            auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
 | 
				
			||||||
        auto* dst_msg_buffer = memory.GetPointer(client_message);
 | 
					            auto* dst_msg_buffer = memory.GetPointer(client_message);
 | 
				
			||||||
        std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
 | 
					            std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        result = ResultSessionClosed;
 | 
					        result = ResultSessionClosed;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -223,7 +281,8 @@ Result KServerSession::SendReply() {
 | 
				
			|||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Result KServerSession::ReceiveRequest() {
 | 
					Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context,
 | 
				
			||||||
 | 
					                                      std::weak_ptr<SessionRequestManager> manager) {
 | 
				
			||||||
    // Lock the session.
 | 
					    // Lock the session.
 | 
				
			||||||
    KScopedLightLock lk{m_lock};
 | 
					    KScopedLightLock lk{m_lock};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -267,12 +326,22 @@ Result KServerSession::ReceiveRequest() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Receive the message.
 | 
					    // Receive the message.
 | 
				
			||||||
    Core::Memory::Memory& memory{kernel.System().Memory()};
 | 
					    Core::Memory::Memory& memory{kernel.System().Memory()};
 | 
				
			||||||
    KThread* server_thread{GetCurrentThreadPointer(kernel)};
 | 
					    if (out_context != nullptr) {
 | 
				
			||||||
    UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
 | 
					        // HLE request.
 | 
				
			||||||
 | 
					        u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(client_message))};
 | 
				
			||||||
 | 
					        *out_context = std::make_shared<HLERequestContext>(kernel, memory, this, client_thread);
 | 
				
			||||||
 | 
					        (*out_context)->SetSessionRequestManager(manager);
 | 
				
			||||||
 | 
					        (*out_context)
 | 
				
			||||||
 | 
					            ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(),
 | 
				
			||||||
 | 
					                                                cmd_buf);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        KThread* server_thread{GetCurrentThreadPointer(kernel)};
 | 
				
			||||||
 | 
					        UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto* src_msg_buffer = memory.GetPointer(client_message);
 | 
					        auto* src_msg_buffer = memory.GetPointer(client_message);
 | 
				
			||||||
    auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
 | 
					        auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
 | 
				
			||||||
    std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
 | 
					        std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // We succeeded.
 | 
					    // We succeeded.
 | 
				
			||||||
    return ResultSuccess;
 | 
					    return ResultSuccess;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
 | 
					// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
@@ -16,21 +16,11 @@
 | 
				
			|||||||
#include "core/hle/kernel/k_synchronization_object.h"
 | 
					#include "core/hle/kernel/k_synchronization_object.h"
 | 
				
			||||||
#include "core/hle/result.h"
 | 
					#include "core/hle/result.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Core::Memory {
 | 
					 | 
				
			||||||
class Memory;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Core::Timing {
 | 
					 | 
				
			||||||
class CoreTiming;
 | 
					 | 
				
			||||||
struct EventType;
 | 
					 | 
				
			||||||
} // namespace Core::Timing
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Kernel {
 | 
					namespace Kernel {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HLERequestContext;
 | 
					class HLERequestContext;
 | 
				
			||||||
class KernelCore;
 | 
					class KernelCore;
 | 
				
			||||||
class KSession;
 | 
					class KSession;
 | 
				
			||||||
class SessionRequestHandler;
 | 
					 | 
				
			||||||
class SessionRequestManager;
 | 
					class SessionRequestManager;
 | 
				
			||||||
class KThread;
 | 
					class KThread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -46,8 +36,7 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    void Destroy() override;
 | 
					    void Destroy() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Initialize(KSession* parent_session_, std::string&& name_,
 | 
					    void Initialize(KSession* parent_session_, std::string&& name_);
 | 
				
			||||||
                    std::shared_ptr<SessionRequestManager> manager_);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    KSession* GetParent() {
 | 
					    KSession* GetParent() {
 | 
				
			||||||
        return parent;
 | 
					        return parent;
 | 
				
			||||||
@@ -60,32 +49,20 @@ public:
 | 
				
			|||||||
    bool IsSignaled() const override;
 | 
					    bool IsSignaled() const override;
 | 
				
			||||||
    void OnClientClosed();
 | 
					    void OnClientClosed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Gets the session request manager, which forwards requests to the underlying service
 | 
					 | 
				
			||||||
    std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() {
 | 
					 | 
				
			||||||
        return manager;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// TODO: flesh these out to match the real kernel
 | 
					    /// TODO: flesh these out to match the real kernel
 | 
				
			||||||
    Result OnRequest(KSessionRequest* request);
 | 
					    Result OnRequest(KSessionRequest* request);
 | 
				
			||||||
    Result SendReply();
 | 
					    Result SendReply(bool is_hle = false);
 | 
				
			||||||
    Result ReceiveRequest();
 | 
					    Result ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context = nullptr,
 | 
				
			||||||
 | 
					                          std::weak_ptr<SessionRequestManager> manager = {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Result SendReplyHLE() {
 | 
				
			||||||
 | 
					        return SendReply(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    /// Frees up waiting client sessions when this server session is about to die
 | 
					    /// Frees up waiting client sessions when this server session is about to die
 | 
				
			||||||
    void CleanupRequests();
 | 
					    void CleanupRequests();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Queues a sync request from the emulated application.
 | 
					 | 
				
			||||||
    Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Completes a sync request from the emulated application.
 | 
					 | 
				
			||||||
    Result CompleteSyncRequest(HLERequestContext& context);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// This session's HLE request handlers; if nullptr, this is not an HLE server
 | 
					 | 
				
			||||||
    std::shared_ptr<SessionRequestManager> manager;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// When set to True, converts the session to a domain at the end of the command
 | 
					 | 
				
			||||||
    bool convert_to_domain{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// KSession that owns this KServerSession
 | 
					    /// KSession that owns this KServerSession
 | 
				
			||||||
    KSession* parent{};
 | 
					    KSession* parent{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,8 +13,7 @@ KSession::KSession(KernelCore& kernel_)
 | 
				
			|||||||
    : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
 | 
					    : KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
 | 
				
			||||||
KSession::~KSession() = default;
 | 
					KSession::~KSession() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void KSession::Initialize(KClientPort* port_, const std::string& name_,
 | 
					void KSession::Initialize(KClientPort* port_, const std::string& name_) {
 | 
				
			||||||
                          std::shared_ptr<SessionRequestManager> manager_) {
 | 
					 | 
				
			||||||
    // Increment reference count.
 | 
					    // Increment reference count.
 | 
				
			||||||
    // Because reference count is one on creation, this will result
 | 
					    // Because reference count is one on creation, this will result
 | 
				
			||||||
    // in a reference count of two. Thus, when both server and client are closed
 | 
					    // in a reference count of two. Thus, when both server and client are closed
 | 
				
			||||||
@@ -26,7 +25,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_,
 | 
				
			|||||||
    KAutoObject::Create(std::addressof(client));
 | 
					    KAutoObject::Create(std::addressof(client));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Initialize our sub sessions.
 | 
					    // Initialize our sub sessions.
 | 
				
			||||||
    server.Initialize(this, name_ + ":Server", manager_);
 | 
					    server.Initialize(this, name_ + ":Server");
 | 
				
			||||||
    client.Initialize(this, name_ + ":Client");
 | 
					    client.Initialize(this, name_ + ":Client");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Set state and name.
 | 
					    // Set state and name.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,8 +21,7 @@ public:
 | 
				
			|||||||
    explicit KSession(KernelCore& kernel_);
 | 
					    explicit KSession(KernelCore& kernel_);
 | 
				
			||||||
    ~KSession() override;
 | 
					    ~KSession() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Initialize(KClientPort* port_, const std::string& name_,
 | 
					    void Initialize(KClientPort* port_, const std::string& name_);
 | 
				
			||||||
                    std::shared_ptr<SessionRequestManager> manager_ = nullptr);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void Finalize() override;
 | 
					    void Finalize() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,7 +60,6 @@ struct KernelCore::Impl {
 | 
				
			|||||||
        global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
 | 
					        global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
 | 
				
			||||||
        global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
 | 
					        global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
 | 
				
			||||||
        global_handle_table->Initialize(KHandleTable::MaxTableSize);
 | 
					        global_handle_table->Initialize(KHandleTable::MaxTableSize);
 | 
				
			||||||
        default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        is_phantom_mode_for_singlecore = false;
 | 
					        is_phantom_mode_for_singlecore = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -86,6 +85,8 @@ struct KernelCore::Impl {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        RegisterHostThread();
 | 
					        RegisterHostThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void InitializeCores() {
 | 
					    void InitializeCores() {
 | 
				
			||||||
@@ -184,17 +185,6 @@ struct KernelCore::Impl {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void CloseServices() {
 | 
					    void CloseServices() {
 | 
				
			||||||
        // Close all open server sessions and ports.
 | 
					 | 
				
			||||||
        std::unordered_set<KAutoObject*> server_objects_;
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            std::scoped_lock lk(server_objects_lock);
 | 
					 | 
				
			||||||
            server_objects_ = server_objects;
 | 
					 | 
				
			||||||
            server_objects.clear();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        for (auto* server_object : server_objects_) {
 | 
					 | 
				
			||||||
            server_object->Close();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Ensures all service threads gracefully shutdown.
 | 
					        // Ensures all service threads gracefully shutdown.
 | 
				
			||||||
        ClearServiceThreads();
 | 
					        ClearServiceThreads();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -346,6 +336,8 @@ struct KernelCore::Impl {
 | 
				
			|||||||
        return this_id;
 | 
					        return this_id;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static inline thread_local bool is_phantom_mode_for_singlecore{false};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool IsPhantomModeForSingleCore() const {
 | 
					    bool IsPhantomModeForSingleCore() const {
 | 
				
			||||||
        return is_phantom_mode_for_singlecore;
 | 
					        return is_phantom_mode_for_singlecore;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -698,24 +690,21 @@ struct KernelCore::Impl {
 | 
				
			|||||||
            return {};
 | 
					            return {};
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        KClientPort* port = &search->second(system.ServiceManager(), system);
 | 
					        return &search->second(system.ServiceManager(), system);
 | 
				
			||||||
        RegisterServerObject(&port->GetParent()->GetServerPort());
 | 
					 | 
				
			||||||
        return port;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void RegisterServerObject(KAutoObject* server_object) {
 | 
					    void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
 | 
				
			||||||
        std::scoped_lock lk(server_objects_lock);
 | 
					        auto search = service_interface_handlers.find(name);
 | 
				
			||||||
        server_objects.insert(server_object);
 | 
					        if (search == service_interface_handlers.end()) {
 | 
				
			||||||
    }
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void UnregisterServerObject(KAutoObject* server_object) {
 | 
					        search->second(system.ServiceManager(), server_port);
 | 
				
			||||||
        std::scoped_lock lk(server_objects_lock);
 | 
					 | 
				
			||||||
        server_objects.erase(server_object);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
 | 
					    std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
 | 
				
			||||||
                                                             const std::string& name) {
 | 
					                                                             const std::string& name) {
 | 
				
			||||||
        auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
 | 
					        auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        service_threads_manager.QueueWork(
 | 
					        service_threads_manager.QueueWork(
 | 
				
			||||||
            [this, service_thread]() { service_threads.emplace(service_thread); });
 | 
					            [this, service_thread]() { service_threads.emplace(service_thread); });
 | 
				
			||||||
@@ -745,7 +734,6 @@ struct KernelCore::Impl {
 | 
				
			|||||||
        service_thread_barrier.Sync();
 | 
					        service_thread_barrier.Sync();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::mutex server_objects_lock;
 | 
					 | 
				
			||||||
    std::mutex registered_objects_lock;
 | 
					    std::mutex registered_objects_lock;
 | 
				
			||||||
    std::mutex registered_in_use_objects_lock;
 | 
					    std::mutex registered_in_use_objects_lock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -774,8 +762,8 @@ struct KernelCore::Impl {
 | 
				
			|||||||
    /// Map of named ports managed by the kernel, which can be retrieved using
 | 
					    /// Map of named ports managed by the kernel, which can be retrieved using
 | 
				
			||||||
    /// the ConnectToPort SVC.
 | 
					    /// the ConnectToPort SVC.
 | 
				
			||||||
    std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
 | 
					    std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
 | 
				
			||||||
 | 
					    std::unordered_map<std::string, ServiceInterfaceHandlerFn> service_interface_handlers;
 | 
				
			||||||
    NamedPortTable named_ports;
 | 
					    NamedPortTable named_ports;
 | 
				
			||||||
    std::unordered_set<KAutoObject*> server_objects;
 | 
					 | 
				
			||||||
    std::unordered_set<KAutoObject*> registered_objects;
 | 
					    std::unordered_set<KAutoObject*> registered_objects;
 | 
				
			||||||
    std::unordered_set<KAutoObject*> registered_in_use_objects;
 | 
					    std::unordered_set<KAutoObject*> registered_in_use_objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -814,7 +802,6 @@ struct KernelCore::Impl {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    bool is_multicore{};
 | 
					    bool is_multicore{};
 | 
				
			||||||
    std::atomic_bool is_shutting_down{};
 | 
					    std::atomic_bool is_shutting_down{};
 | 
				
			||||||
    bool is_phantom_mode_for_singlecore{};
 | 
					 | 
				
			||||||
    u32 single_core_thread_id{};
 | 
					    u32 single_core_thread_id{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
 | 
					    std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
 | 
				
			||||||
@@ -981,16 +968,17 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&
 | 
				
			|||||||
    impl->service_interface_factory.emplace(std::move(name), factory);
 | 
					    impl->service_interface_factory.emplace(std::move(name), factory);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void KernelCore::RegisterInterfaceForNamedService(std::string name,
 | 
				
			||||||
 | 
					                                                  ServiceInterfaceHandlerFn&& handler) {
 | 
				
			||||||
 | 
					    impl->service_interface_handlers.emplace(std::move(name), handler);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
 | 
					KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
 | 
				
			||||||
    return impl->CreateNamedServicePort(std::move(name));
 | 
					    return impl->CreateNamedServicePort(std::move(name));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void KernelCore::RegisterServerObject(KAutoObject* server_object) {
 | 
					void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
 | 
				
			||||||
    impl->RegisterServerObject(server_object);
 | 
					    impl->RegisterNamedServiceHandler(std::move(name), server_port);
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
 | 
					 | 
				
			||||||
    impl->UnregisterServerObject(server_object);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void KernelCore::RegisterKernelObject(KAutoObject* object) {
 | 
					void KernelCore::RegisterKernelObject(KAutoObject* object) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,6 +45,7 @@ class KPort;
 | 
				
			|||||||
class KProcess;
 | 
					class KProcess;
 | 
				
			||||||
class KResourceLimit;
 | 
					class KResourceLimit;
 | 
				
			||||||
class KScheduler;
 | 
					class KScheduler;
 | 
				
			||||||
 | 
					class KServerPort;
 | 
				
			||||||
class KServerSession;
 | 
					class KServerSession;
 | 
				
			||||||
class KSession;
 | 
					class KSession;
 | 
				
			||||||
class KSessionRequest;
 | 
					class KSessionRequest;
 | 
				
			||||||
@@ -63,6 +64,8 @@ class TimeManager;
 | 
				
			|||||||
using ServiceInterfaceFactory =
 | 
					using ServiceInterfaceFactory =
 | 
				
			||||||
    std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
 | 
					    std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using ServiceInterfaceHandlerFn = std::function<void(Service::SM::ServiceManager&, KServerPort*)>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Init {
 | 
					namespace Init {
 | 
				
			||||||
struct KSlabResourceCounts;
 | 
					struct KSlabResourceCounts;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -192,16 +195,14 @@ public:
 | 
				
			|||||||
    /// Registers a named HLE service, passing a factory used to open a port to that service.
 | 
					    /// Registers a named HLE service, passing a factory used to open a port to that service.
 | 
				
			||||||
    void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory);
 | 
					    void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Registers a setup function for the named HLE service.
 | 
				
			||||||
 | 
					    void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Opens a port to a service previously registered with RegisterNamedService.
 | 
					    /// Opens a port to a service previously registered with RegisterNamedService.
 | 
				
			||||||
    KClientPort* CreateNamedServicePort(std::string name);
 | 
					    KClientPort* CreateNamedServicePort(std::string name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Registers a server session or port with the gobal emulation state, to be freed on shutdown.
 | 
					    /// Accepts a session on a port created by CreateNamedServicePort.
 | 
				
			||||||
    /// This is necessary because we do not emulate processes for HLE sessions and ports.
 | 
					    void RegisterNamedServiceHandler(std::string name, KServerPort* server_port);
 | 
				
			||||||
    void RegisterServerObject(KAutoObject* server_object);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Unregisters a server session or port previously registered with RegisterServerSession when
 | 
					 | 
				
			||||||
    /// it was destroyed during the current emulation session.
 | 
					 | 
				
			||||||
    void UnregisterServerObject(KAutoObject* server_object);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Registers all kernel objects with the global emulation state, this is purely for tracking
 | 
					    /// Registers all kernel objects with the global emulation state, this is purely for tracking
 | 
				
			||||||
    /// leaks after emulation has been shutdown.
 | 
					    /// leaks after emulation has been shutdown.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,15 +1,18 @@
 | 
				
			|||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
 | 
					// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
 | 
				
			||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <condition_variable>
 | 
					 | 
				
			||||||
#include <functional>
 | 
					#include <functional>
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
#include <mutex>
 | 
					#include <mutex>
 | 
				
			||||||
#include <thread>
 | 
					#include <thread>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include <queue>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "common/scope_exit.h"
 | 
					#include "common/scope_exit.h"
 | 
				
			||||||
#include "common/thread.h"
 | 
					#include "common/thread.h"
 | 
				
			||||||
 | 
					#include "core/hle/ipc_helpers.h"
 | 
				
			||||||
 | 
					#include "core/hle/kernel/hle_ipc.h"
 | 
				
			||||||
 | 
					#include "core/hle/kernel/k_event.h"
 | 
				
			||||||
 | 
					#include "core/hle/kernel/k_scoped_resource_reservation.h"
 | 
				
			||||||
#include "core/hle/kernel/k_session.h"
 | 
					#include "core/hle/kernel/k_session.h"
 | 
				
			||||||
#include "core/hle/kernel/k_thread.h"
 | 
					#include "core/hle/kernel/k_thread.h"
 | 
				
			||||||
#include "core/hle/kernel/kernel.h"
 | 
					#include "core/hle/kernel/kernel.h"
 | 
				
			||||||
@@ -19,101 +22,198 @@ namespace Kernel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ServiceThread::Impl final {
 | 
					class ServiceThread::Impl final {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
 | 
					    explicit Impl(KernelCore& kernel, const std::string& service_name);
 | 
				
			||||||
    ~Impl();
 | 
					    ~Impl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
 | 
					    void WaitAndProcessImpl();
 | 
				
			||||||
 | 
					    void SessionClosed(KServerSession* server_session,
 | 
				
			||||||
 | 
					                       std::shared_ptr<SessionRequestManager> manager);
 | 
				
			||||||
 | 
					    void LoopProcess();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void RegisterServerSession(KServerSession* session,
 | 
				
			||||||
 | 
					                               std::shared_ptr<SessionRequestManager> manager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    std::vector<std::jthread> threads;
 | 
					    KernelCore& kernel;
 | 
				
			||||||
    std::queue<std::function<void()>> requests;
 | 
					
 | 
				
			||||||
    std::mutex queue_mutex;
 | 
					    std::jthread m_thread;
 | 
				
			||||||
    std::condition_variable_any condition;
 | 
					    std::mutex m_session_mutex;
 | 
				
			||||||
    const std::string service_name;
 | 
					    std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions;
 | 
				
			||||||
 | 
					    KEvent* m_wakeup_event;
 | 
				
			||||||
 | 
					    KProcess* m_process;
 | 
				
			||||||
 | 
					    std::atomic<bool> m_shutdown_requested;
 | 
				
			||||||
 | 
					    const std::string m_service_name;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
 | 
					void ServiceThread::Impl::WaitAndProcessImpl() {
 | 
				
			||||||
    : service_name{name} {
 | 
					    // Create local list of waitable sessions.
 | 
				
			||||||
    for (std::size_t i = 0; i < num_threads; ++i) {
 | 
					    std::vector<KSynchronizationObject*> objs;
 | 
				
			||||||
        threads.emplace_back([this, &kernel](std::stop_token stop_token) {
 | 
					    std::vector<std::shared_ptr<SessionRequestManager>> managers;
 | 
				
			||||||
            Common::SetCurrentThreadName(std::string{service_name}.c_str());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Wait for first request before trying to acquire a render context
 | 
					    {
 | 
				
			||||||
            {
 | 
					        // Lock to get the set.
 | 
				
			||||||
                std::unique_lock lock{queue_mutex};
 | 
					        std::scoped_lock lk{m_session_mutex};
 | 
				
			||||||
                condition.wait(lock, stop_token, [this] { return !requests.empty(); });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (stop_token.stop_requested()) {
 | 
					        // Reserve the needed quantity.
 | 
				
			||||||
                return;
 | 
					        objs.reserve(m_sessions.size() + 1);
 | 
				
			||||||
            }
 | 
					        managers.reserve(m_sessions.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Allocate a dummy guest thread for this host thread.
 | 
					        // Copy to our local list.
 | 
				
			||||||
            kernel.RegisterHostThread();
 | 
					        for (const auto& [session, manager] : m_sessions) {
 | 
				
			||||||
 | 
					            objs.push_back(session);
 | 
				
			||||||
 | 
					            managers.push_back(manager);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            while (true) {
 | 
					        // Insert the wakeup event at the end.
 | 
				
			||||||
                std::function<void()> task;
 | 
					        objs.push_back(&m_wakeup_event->GetReadableEvent());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                {
 | 
					    // Wait on the list of sessions.
 | 
				
			||||||
                    std::unique_lock lock{queue_mutex};
 | 
					    s32 index{-1};
 | 
				
			||||||
                    condition.wait(lock, stop_token, [this] { return !requests.empty(); });
 | 
					    Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(),
 | 
				
			||||||
 | 
					                                             static_cast<s32>(objs.size()), -1);
 | 
				
			||||||
 | 
					    ASSERT(!rc.IsFailure());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (stop_token.stop_requested()) {
 | 
					    // If this was the wakeup event, clear it and finish.
 | 
				
			||||||
                        return;
 | 
					    if (index >= static_cast<s64>(objs.size() - 1)) {
 | 
				
			||||||
                    }
 | 
					        m_wakeup_event->Clear();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (requests.empty()) {
 | 
					    // This event is from a server session.
 | 
				
			||||||
                        continue;
 | 
					    auto* server_session = static_cast<KServerSession*>(objs[index]);
 | 
				
			||||||
                    }
 | 
					    auto& manager = managers[index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    task = std::move(requests.front());
 | 
					    // Fetch the HLE request context.
 | 
				
			||||||
                    requests.pop();
 | 
					    std::shared_ptr<HLERequestContext> context;
 | 
				
			||||||
                }
 | 
					    rc = server_session->ReceiveRequest(&context, manager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                task();
 | 
					    // If the session was closed, handle that.
 | 
				
			||||||
            }
 | 
					    if (rc == ResultSessionClosed) {
 | 
				
			||||||
        });
 | 
					        SessionClosed(server_session, manager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Finish.
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: handle other cases
 | 
				
			||||||
 | 
					    ASSERT(rc == ResultSuccess);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Perform the request.
 | 
				
			||||||
 | 
					    Result service_rc = manager->CompleteSyncRequest(server_session, *context);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Reply to the client.
 | 
				
			||||||
 | 
					    rc = server_session->SendReplyHLE();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) {
 | 
				
			||||||
 | 
					        SessionClosed(server_session, manager);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: handle other cases
 | 
				
			||||||
 | 
					    ASSERT(rc == ResultSuccess);
 | 
				
			||||||
 | 
					    ASSERT(service_rc == ResultSuccess);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
 | 
				
			||||||
 | 
					                                        std::shared_ptr<SessionRequestManager> manager) {
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // Lock to get the set.
 | 
				
			||||||
 | 
					        std::scoped_lock lk{m_session_mutex};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Erase the session.
 | 
				
			||||||
 | 
					        ASSERT(m_sessions.erase(server_session) == 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Close our reference to the server session.
 | 
				
			||||||
 | 
					    server_session->Close();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ServiceThread::Impl::LoopProcess() {
 | 
				
			||||||
 | 
					    Common::SetCurrentThreadName(m_service_name.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    kernel.RegisterHostThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (!m_shutdown_requested.load()) {
 | 
				
			||||||
 | 
					        WaitAndProcessImpl();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ServiceThread::Impl::QueueSyncRequest(KSession& session,
 | 
					void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session,
 | 
				
			||||||
                                           std::shared_ptr<HLERequestContext>&& context) {
 | 
					                                                std::shared_ptr<SessionRequestManager> manager) {
 | 
				
			||||||
 | 
					    // Open the server session.
 | 
				
			||||||
 | 
					    server_session->Open();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        std::unique_lock lock{queue_mutex};
 | 
					        // Lock to get the set.
 | 
				
			||||||
 | 
					        std::scoped_lock lk{m_session_mutex};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        auto* server_session{&session.GetServerSession()};
 | 
					        // Insert the session and manager.
 | 
				
			||||||
 | 
					        m_sessions[server_session] = manager;
 | 
				
			||||||
        // Open a reference to the session to ensure it is not closes while the service request
 | 
					 | 
				
			||||||
        // completes asynchronously.
 | 
					 | 
				
			||||||
        server_session->Open();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        requests.emplace([server_session, context{std::move(context)}]() {
 | 
					 | 
				
			||||||
            // Close the reference.
 | 
					 | 
				
			||||||
            SCOPE_EXIT({ server_session->Close(); });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Complete the service request.
 | 
					 | 
				
			||||||
            server_session->CompleteSyncRequest(*context);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    condition.notify_one();
 | 
					
 | 
				
			||||||
 | 
					    // Signal the wakeup event.
 | 
				
			||||||
 | 
					    m_wakeup_event->Signal();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ServiceThread::Impl::~Impl() {
 | 
					ServiceThread::Impl::~Impl() {
 | 
				
			||||||
    condition.notify_all();
 | 
					    // Shut down the processing thread.
 | 
				
			||||||
    for (auto& thread : threads) {
 | 
					    m_shutdown_requested.store(true);
 | 
				
			||||||
        thread.request_stop();
 | 
					    m_wakeup_event->Signal();
 | 
				
			||||||
        thread.join();
 | 
					    m_thread.join();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Lock mutex.
 | 
				
			||||||
 | 
					    m_session_mutex.lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Close all remaining sessions.
 | 
				
			||||||
 | 
					    for (const auto& [server_session, manager] : m_sessions) {
 | 
				
			||||||
 | 
					        server_session->Close();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Destroy remaining managers.
 | 
				
			||||||
 | 
					    m_sessions.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Close event.
 | 
				
			||||||
 | 
					    m_wakeup_event->GetReadableEvent().Close();
 | 
				
			||||||
 | 
					    m_wakeup_event->Close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Close process.
 | 
				
			||||||
 | 
					    m_process->Close();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name)
 | 
					ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
 | 
				
			||||||
    : impl{std::make_unique<Impl>(kernel, num_threads, name)} {}
 | 
					    : kernel{kernel_}, m_service_name{service_name} {
 | 
				
			||||||
 | 
					    // Initialize process.
 | 
				
			||||||
 | 
					    m_process = KProcess::Create(kernel);
 | 
				
			||||||
 | 
					    KProcess::Initialize(m_process, kernel.System(), service_name,
 | 
				
			||||||
 | 
					                         KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Reserve a new event from the process resource limit
 | 
				
			||||||
 | 
					    KScopedResourceReservation event_reservation(m_process, LimitableResource::Events);
 | 
				
			||||||
 | 
					    ASSERT(event_reservation.Succeeded());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Initialize event.
 | 
				
			||||||
 | 
					    m_wakeup_event = KEvent::Create(kernel);
 | 
				
			||||||
 | 
					    m_wakeup_event->Initialize(m_process);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Commit the event reservation.
 | 
				
			||||||
 | 
					    event_reservation.Commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Register the event.
 | 
				
			||||||
 | 
					    KEvent::Register(kernel, m_wakeup_event);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Start thread.
 | 
				
			||||||
 | 
					    m_thread = std::jthread([this] { LoopProcess(); });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)
 | 
				
			||||||
 | 
					    : impl{std::make_unique<Impl>(kernel, name)} {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ServiceThread::~ServiceThread() = default;
 | 
					ServiceThread::~ServiceThread() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ServiceThread::QueueSyncRequest(KSession& session,
 | 
					void ServiceThread::RegisterServerSession(KServerSession* session,
 | 
				
			||||||
                                     std::shared_ptr<HLERequestContext>&& context) {
 | 
					                                          std::shared_ptr<SessionRequestManager> manager) {
 | 
				
			||||||
    impl->QueueSyncRequest(session, std::move(context));
 | 
					    impl->RegisterServerSession(session, manager);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace Kernel
 | 
					} // namespace Kernel
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,13 +11,15 @@ namespace Kernel {
 | 
				
			|||||||
class HLERequestContext;
 | 
					class HLERequestContext;
 | 
				
			||||||
class KernelCore;
 | 
					class KernelCore;
 | 
				
			||||||
class KSession;
 | 
					class KSession;
 | 
				
			||||||
 | 
					class SessionRequestManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ServiceThread final {
 | 
					class ServiceThread final {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name);
 | 
					    explicit ServiceThread(KernelCore& kernel, const std::string& name);
 | 
				
			||||||
    ~ServiceThread();
 | 
					    ~ServiceThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
 | 
					    void RegisterServerSession(KServerSession* session,
 | 
				
			||||||
 | 
					                               std::shared_ptr<SessionRequestManager> manager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    class Impl;
 | 
					    class Impl;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@
 | 
				
			|||||||
#include "core/hle/kernel/k_memory_block.h"
 | 
					#include "core/hle/kernel/k_memory_block.h"
 | 
				
			||||||
#include "core/hle/kernel/k_memory_layout.h"
 | 
					#include "core/hle/kernel/k_memory_layout.h"
 | 
				
			||||||
#include "core/hle/kernel/k_page_table.h"
 | 
					#include "core/hle/kernel/k_page_table.h"
 | 
				
			||||||
 | 
					#include "core/hle/kernel/k_port.h"
 | 
				
			||||||
#include "core/hle/kernel/k_process.h"
 | 
					#include "core/hle/kernel/k_process.h"
 | 
				
			||||||
#include "core/hle/kernel/k_readable_event.h"
 | 
					#include "core/hle/kernel/k_readable_event.h"
 | 
				
			||||||
#include "core/hle/kernel/k_resource_limit.h"
 | 
					#include "core/hle/kernel/k_resource_limit.h"
 | 
				
			||||||
@@ -382,9 +383,9 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Create a session.
 | 
					    // Create a session.
 | 
				
			||||||
    KClientSession* session{};
 | 
					    KClientSession* session{};
 | 
				
			||||||
    R_TRY(port->CreateSession(std::addressof(session),
 | 
					    R_TRY(port->CreateSession(std::addressof(session)));
 | 
				
			||||||
                              std::make_shared<SessionRequestManager>(kernel)));
 | 
					
 | 
				
			||||||
    port->Close();
 | 
					    kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Register the session in the table, close the extra reference.
 | 
					    // Register the session in the table, close the extra reference.
 | 
				
			||||||
    handle_table.Register(*out, session);
 | 
					    handle_table.Register(*out, session);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,6 +99,12 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
 | 
				
			|||||||
ServiceFrameworkBase::~ServiceFrameworkBase() {
 | 
					ServiceFrameworkBase::~ServiceFrameworkBase() {
 | 
				
			||||||
    // Wait for other threads to release access before destroying
 | 
					    // Wait for other threads to release access before destroying
 | 
				
			||||||
    const auto guard = LockService();
 | 
					    const auto guard = LockService();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (named_port != nullptr) {
 | 
				
			||||||
 | 
					        named_port->GetClientPort().Close();
 | 
				
			||||||
 | 
					        named_port->GetServerPort().Close();
 | 
				
			||||||
 | 
					        named_port = nullptr;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
 | 
					void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
 | 
				
			||||||
@@ -113,15 +119,16 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
 | 
				
			|||||||
Kernel::KClientPort& ServiceFrameworkBase::CreatePort() {
 | 
					Kernel::KClientPort& ServiceFrameworkBase::CreatePort() {
 | 
				
			||||||
    const auto guard = LockService();
 | 
					    const auto guard = LockService();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ASSERT(!service_registered);
 | 
					    if (named_port == nullptr) {
 | 
				
			||||||
 | 
					        ASSERT(!service_registered);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto* port = Kernel::KPort::Create(kernel);
 | 
					        named_port = Kernel::KPort::Create(kernel);
 | 
				
			||||||
    port->Initialize(max_sessions, false, service_name);
 | 
					        named_port->Initialize(max_sessions, false, service_name);
 | 
				
			||||||
    port->GetServerPort().SetSessionHandler(shared_from_this());
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    service_registered = true;
 | 
					        service_registered = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return port->GetClientPort();
 | 
					    return named_port->GetClientPort();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
 | 
					void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) {
 | 
				
			||||||
@@ -199,7 +206,6 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
 | 
				
			|||||||
    switch (ctx.GetCommandType()) {
 | 
					    switch (ctx.GetCommandType()) {
 | 
				
			||||||
    case IPC::CommandType::Close:
 | 
					    case IPC::CommandType::Close:
 | 
				
			||||||
    case IPC::CommandType::TIPC_Close: {
 | 
					    case IPC::CommandType::TIPC_Close: {
 | 
				
			||||||
        session.Close();
 | 
					 | 
				
			||||||
        IPC::ResponseBuilder rb{ctx, 2};
 | 
					        IPC::ResponseBuilder rb{ctx, 2};
 | 
				
			||||||
        rb.Push(ResultSuccess);
 | 
					        rb.Push(ResultSuccess);
 | 
				
			||||||
        result = IPC::ERR_REMOTE_PROCESS_DEAD;
 | 
					        result = IPC::ERR_REMOTE_PROCESS_DEAD;
 | 
				
			||||||
@@ -244,6 +250,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
 | 
				
			|||||||
    system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
 | 
					    system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory);
 | 
					    system.Kernel().RegisterNamedService("sm:", SM::ServiceManager::InterfaceFactory);
 | 
				
			||||||
 | 
					    system.Kernel().RegisterInterfaceForNamedService("sm:", SM::ServiceManager::SessionHandler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Account::InstallInterfaces(system);
 | 
					    Account::InstallInterfaces(system);
 | 
				
			||||||
    AM::InstallInterfaces(*sm, *nv_flinger, system);
 | 
					    AM::InstallInterfaces(*sm, *nv_flinger, system);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ class System;
 | 
				
			|||||||
namespace Kernel {
 | 
					namespace Kernel {
 | 
				
			||||||
class HLERequestContext;
 | 
					class HLERequestContext;
 | 
				
			||||||
class KClientPort;
 | 
					class KClientPort;
 | 
				
			||||||
 | 
					class KPort;
 | 
				
			||||||
class KServerSession;
 | 
					class KServerSession;
 | 
				
			||||||
class ServiceThread;
 | 
					class ServiceThread;
 | 
				
			||||||
} // namespace Kernel
 | 
					} // namespace Kernel
 | 
				
			||||||
@@ -98,6 +99,9 @@ protected:
 | 
				
			|||||||
    /// Identifier string used to connect to the service.
 | 
					    /// Identifier string used to connect to the service.
 | 
				
			||||||
    std::string service_name;
 | 
					    std::string service_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Port used by ManageNamedPort.
 | 
				
			||||||
 | 
					    Kernel::KPort* named_port{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    template <typename T>
 | 
					    template <typename T>
 | 
				
			||||||
    friend class ServiceFramework;
 | 
					    friend class ServiceFramework;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,13 @@ constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6);
 | 
				
			|||||||
constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
 | 
					constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
 | 
					ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
 | 
				
			||||||
ServiceManager::~ServiceManager() = default;
 | 
					
 | 
				
			||||||
 | 
					ServiceManager::~ServiceManager() {
 | 
				
			||||||
 | 
					    for (auto& [name, port] : service_ports) {
 | 
				
			||||||
 | 
					        port->GetClientPort().Close();
 | 
				
			||||||
 | 
					        port->GetServerPort().Close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
 | 
					void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
 | 
				
			||||||
    controller_interface->InvokeRequest(context);
 | 
					    controller_interface->InvokeRequest(context);
 | 
				
			||||||
@@ -43,6 +49,10 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core
 | 
				
			|||||||
    return self.sm_interface->CreatePort();
 | 
					    return self.sm_interface->CreatePort();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ServiceManager::SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port) {
 | 
				
			||||||
 | 
					    self.sm_interface->AcceptSession(server_port);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
 | 
					Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
 | 
				
			||||||
                                       Kernel::SessionRequestHandlerPtr handler) {
 | 
					                                       Kernel::SessionRequestHandlerPtr handler) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -53,7 +63,11 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
 | 
				
			|||||||
        return ERR_ALREADY_REGISTERED;
 | 
					        return ERR_ALREADY_REGISTERED;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    registered_services.emplace(std::move(name), handler);
 | 
					    auto* port = Kernel::KPort::Create(kernel);
 | 
				
			||||||
 | 
					    port->Initialize(ServerSessionCountMax, false, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    service_ports.emplace(name, port);
 | 
				
			||||||
 | 
					    registered_services.emplace(name, handler);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return ResultSuccess;
 | 
					    return ResultSuccess;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -68,24 +82,20 @@ Result ServiceManager::UnregisterService(const std::string& name) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    registered_services.erase(iter);
 | 
					    registered_services.erase(iter);
 | 
				
			||||||
 | 
					    service_ports.erase(name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return ResultSuccess;
 | 
					    return ResultSuccess;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
 | 
					ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name) {
 | 
				
			||||||
    CASCADE_CODE(ValidateServiceName(name));
 | 
					    CASCADE_CODE(ValidateServiceName(name));
 | 
				
			||||||
    auto it = registered_services.find(name);
 | 
					    auto it = service_ports.find(name);
 | 
				
			||||||
    if (it == registered_services.end()) {
 | 
					    if (it == service_ports.end()) {
 | 
				
			||||||
        LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
 | 
					        LOG_ERROR(Service_SM, "Server is not registered! service={}", name);
 | 
				
			||||||
        return ERR_SERVICE_NOT_REGISTERED;
 | 
					        return ERR_SERVICE_NOT_REGISTERED;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto* port = Kernel::KPort::Create(kernel);
 | 
					    return it->second;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    port->Initialize(ServerSessionCountMax, false, name);
 | 
					 | 
				
			||||||
    auto handler = it->second;
 | 
					 | 
				
			||||||
    port->GetServerPort().SetSessionHandler(std::move(handler));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return port;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -144,24 +154,20 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Find the named port.
 | 
					    // Find the named port.
 | 
				
			||||||
    auto port_result = service_manager.GetServicePort(name);
 | 
					    auto port_result = service_manager.GetServicePort(name);
 | 
				
			||||||
    if (port_result.Failed()) {
 | 
					    auto service = service_manager.GetService<Kernel::SessionRequestHandler>(name);
 | 
				
			||||||
 | 
					    if (port_result.Failed() || !service) {
 | 
				
			||||||
        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
 | 
					        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, port_result.Code().raw);
 | 
				
			||||||
        return port_result.Code();
 | 
					        return port_result.Code();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    auto& port = port_result.Unwrap();
 | 
					    auto& port = port_result.Unwrap();
 | 
				
			||||||
    SCOPE_EXIT({
 | 
					 | 
				
			||||||
        port->GetClientPort().Close();
 | 
					 | 
				
			||||||
        port->GetServerPort().Close();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create a new session.
 | 
					    // Create a new session.
 | 
				
			||||||
    Kernel::KClientSession* session{};
 | 
					    Kernel::KClientSession* session{};
 | 
				
			||||||
    if (const auto result = port->GetClientPort().CreateSession(
 | 
					    if (const auto result = port->GetClientPort().CreateSession(&session); result.IsError()) {
 | 
				
			||||||
            std::addressof(session), std::make_shared<Kernel::SessionRequestManager>(kernel));
 | 
					 | 
				
			||||||
        result.IsError()) {
 | 
					 | 
				
			||||||
        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
 | 
					        LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, result.raw);
 | 
				
			||||||
        return result;
 | 
					        return result;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    service->AcceptSession(&port->GetServerPort());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
 | 
					    LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,6 +51,7 @@ private:
 | 
				
			|||||||
class ServiceManager {
 | 
					class ServiceManager {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system);
 | 
					    static Kernel::KClientPort& InterfaceFactory(ServiceManager& self, Core::System& system);
 | 
				
			||||||
 | 
					    static void SessionHandler(ServiceManager& self, Kernel::KServerPort* server_port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    explicit ServiceManager(Kernel::KernelCore& kernel_);
 | 
					    explicit ServiceManager(Kernel::KernelCore& kernel_);
 | 
				
			||||||
    ~ServiceManager();
 | 
					    ~ServiceManager();
 | 
				
			||||||
@@ -78,6 +79,7 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Map of registered services, retrieved using GetServicePort.
 | 
					    /// Map of registered services, retrieved using GetServicePort.
 | 
				
			||||||
    std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services;
 | 
					    std::unordered_map<std::string, Kernel::SessionRequestHandlerPtr> registered_services;
 | 
				
			||||||
 | 
					    std::unordered_map<std::string, Kernel::KPort*> service_ports;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Kernel context
 | 
					    /// Kernel context
 | 
				
			||||||
    Kernel::KernelCore& kernel;
 | 
					    Kernel::KernelCore& kernel;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,10 +15,9 @@
 | 
				
			|||||||
namespace Service::SM {
 | 
					namespace Service::SM {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
 | 
					void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
 | 
				
			||||||
    ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(),
 | 
					    ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Session is already a domain");
 | 
				
			||||||
               "Session is already a domain");
 | 
					 | 
				
			||||||
    LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
 | 
					    LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
 | 
				
			||||||
    ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd();
 | 
					    ctx.GetManager()->ConvertToDomainOnRequestEnd();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    IPC::ResponseBuilder rb{ctx, 3};
 | 
					    IPC::ResponseBuilder rb{ctx, 3};
 | 
				
			||||||
    rb.Push(ResultSuccess);
 | 
					    rb.Push(ResultSuccess);
 | 
				
			||||||
@@ -29,9 +28,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
 | 
				
			|||||||
    LOG_DEBUG(Service, "called");
 | 
					    LOG_DEBUG(Service, "called");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto& process = *ctx.GetThread().GetOwnerProcess();
 | 
					    auto& process = *ctx.GetThread().GetOwnerProcess();
 | 
				
			||||||
    auto& parent_session = *ctx.Session()->GetParent();
 | 
					    auto session_manager = ctx.GetManager();
 | 
				
			||||||
    auto& session_manager = parent_session.GetServerSession().GetSessionRequestManager();
 | 
					 | 
				
			||||||
    auto& session_handler = session_manager->SessionHandler();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // FIXME: this is duplicated from the SVC, it should just call it instead
 | 
					    // FIXME: this is duplicated from the SVC, it should just call it instead
 | 
				
			||||||
    // once this is a proper process
 | 
					    // once this is a proper process
 | 
				
			||||||
@@ -46,13 +43,14 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
 | 
				
			|||||||
    ASSERT(session != nullptr);
 | 
					    ASSERT(session != nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Initialize the session.
 | 
					    // Initialize the session.
 | 
				
			||||||
    session->Initialize(nullptr, parent_session.GetName(), session_manager);
 | 
					    session->Initialize(nullptr, "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Commit the session reservation.
 | 
					    // Commit the session reservation.
 | 
				
			||||||
    session_reservation.Commit();
 | 
					    session_reservation.Commit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Register the session.
 | 
					    // Register with manager.
 | 
				
			||||||
    session_handler.ClientConnected(&session->GetServerSession());
 | 
					    session_manager->SessionHandler().RegisterSession(&session->GetServerSession(),
 | 
				
			||||||
 | 
					                                                      session_manager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // We succeeded.
 | 
					    // We succeeded.
 | 
				
			||||||
    IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
 | 
					    IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user