summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorLiam <byteslice@airmail.cc>2022-10-16 07:53:56 +0200
committerLiam <byteslice@airmail.cc>2022-10-31 22:44:06 +0100
commit983f2b70741f17f30fe2321451f10cabecc013d2 (patch)
treec1ac3c1033fdeefaabe76590ca204c4c1b2a98cd /src/core/hle/kernel
parentMerge pull request #9159 from liamwhite/kbork (diff)
downloadyuzu-983f2b70741f17f30fe2321451f10cabecc013d2.tar
yuzu-983f2b70741f17f30fe2321451f10cabecc013d2.tar.gz
yuzu-983f2b70741f17f30fe2321451f10cabecc013d2.tar.bz2
yuzu-983f2b70741f17f30fe2321451f10cabecc013d2.tar.lz
yuzu-983f2b70741f17f30fe2321451f10cabecc013d2.tar.xz
yuzu-983f2b70741f17f30fe2321451f10cabecc013d2.tar.zst
yuzu-983f2b70741f17f30fe2321451f10cabecc013d2.zip
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp55
-rw-r--r--src/core/hle/kernel/hle_ipc.h29
-rw-r--r--src/core/hle/kernel/k_client_port.cpp5
-rw-r--r--src/core/hle/kernel/k_client_port.h3
-rw-r--r--src/core/hle/kernel/k_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_port.cpp5
-rw-r--r--src/core/hle/kernel/k_server_port.h19
-rw-r--r--src/core/hle/kernel/k_server_session.cpp187
-rw-r--r--src/core/hle/kernel/k_server_session.h37
-rw-r--r--src/core/hle/kernel/k_session.cpp5
-rw-r--r--src/core/hle/kernel/k_session.h3
-rw-r--r--src/core/hle/kernel/kernel.cpp24
-rw-r--r--src/core/hle/kernel/kernel.h9
-rw-r--r--src/core/hle/kernel/service_thread.cpp232
-rw-r--r--src/core/hle/kernel/service_thread.h6
-rw-r--r--src/core/hle/kernel/svc.cpp6
16 files changed, 381 insertions, 250 deletions
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e4f43a053..fd354d484 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -16,6 +16,7 @@
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_handle_table.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_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -35,7 +36,21 @@ SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* se
}
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_} {}
@@ -92,7 +107,7 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses
}
// 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"
const auto& domain_message_header = context.GetDomainMessageHeader();
@@ -130,31 +145,6 @@ Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_ses
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_,
KServerSession* server_session_, KThread* thread_)
: 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
rp.AlignWithPadding();
- if (Session()->GetSessionRequestManager()->IsDomain() &&
+ if (GetManager()->IsDomain() &&
((command_header->type == IPC::CommandType::Request ||
command_header->type == IPC::CommandType::RequestWithContext) ||
!incoming)) {
@@ -223,7 +213,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
if (incoming || domain_message_header) {
domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
} else {
- if (Session()->GetSessionRequestManager()->IsDomain()) {
+ if (GetManager()->IsDomain()) {
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.
// 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());
for (auto& object : outgoing_domain_objects) {
- server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object));
- cmd_buf[current_offset++] = static_cast<u32_le>(
- server_session->GetSessionRequestManager()->DomainHandlerCount());
+ GetManager()->AppendDomainHandler(std::move(object));
+ cmd_buf[current_offset++] = static_cast<u32_le>(GetManager()->DomainHandlerCount());
}
}
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 1083638a9..67da8e7e1 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -45,11 +45,13 @@ class KAutoObject;
class KernelCore;
class KEvent;
class KHandleTable;
+class KServerPort;
class KProcess;
class KServerSession;
class KThread;
class KReadableEvent;
class KSession;
+class SessionRequestManager;
class ServiceThread;
enum class ThreadWakeupReason;
@@ -76,19 +78,9 @@ public:
virtual Result HandleSyncRequest(Kernel::KServerSession& session,
Kernel::HLERequestContext& context) = 0;
- /**
- * Signals that a client has just connected to this HLE handler and keeps the
- * associated ServerSession alive for the duration of the connection.
- * @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);
+ void AcceptSession(KServerPort* server_port);
+ void RegisterSession(KServerSession* server_session,
+ std::shared_ptr<SessionRequestManager> manager);
std::weak_ptr<ServiceThread> GetServiceThread() const {
return service_thread;
@@ -170,7 +162,6 @@ public:
Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
- Result QueueSyncRequest(KSession* parent, std::shared_ptr<HLERequestContext>&& context);
private:
bool convert_to_domain{};
@@ -350,11 +341,11 @@ public:
template <typename T>
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_) {
- manager = std::move(manager_);
+ manager = manager_;
}
std::string Description() const;
@@ -363,6 +354,10 @@ public:
return *thread;
}
+ std::shared_ptr<SessionRequestManager> GetManager() const {
+ return manager.lock();
+ }
+
private:
friend class IPC::ResponseBuilder;
@@ -396,7 +391,7 @@ private:
u32 handles_offset{};
u32 domain_offset{};
- std::weak_ptr<SessionRequestManager> manager;
+ std::weak_ptr<SessionRequestManager> manager{};
KernelCore& kernel;
Core::Memory::Memory& memory;
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 3cb22ff4d..eaa2e094c 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -58,8 +58,7 @@ bool KClientPort::IsSignaled() const {
return num_sessions < max_sessions;
}
-Result KClientPort::CreateSession(KClientSession** out,
- std::shared_ptr<SessionRequestManager> session_manager) {
+Result KClientPort::CreateSession(KClientSession** out) {
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
LimitableResource::Sessions);
@@ -104,7 +103,7 @@ Result KClientPort::CreateSession(KClientSession** out,
}
// Initialize the session.
- session->Initialize(this, parent->GetName(), session_manager);
+ session->Initialize(this, parent->GetName());
// Commit the session reservation.
session_reservation.Commit();
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index e17eff28f..81046fb86 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -52,8 +52,7 @@ public:
void Destroy() override;
bool IsSignaled() const override;
- Result CreateSession(KClientSession** out,
- std::shared_ptr<SessionRequestManager> session_manager = nullptr);
+ Result CreateSession(KClientSession** out);
private:
std::atomic<s32> num_sessions{};
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index 7a5a9dc2a..77d00ae2c 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -57,12 +57,6 @@ Result KPort::EnqueueSession(KServerSession* session) {
server.EnqueueSession(session);
- if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
- session_ptr->ClientConnected(server.AcceptSession());
- } else {
- ASSERT(false);
- }
-
return ResultSuccess;
}
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index e968f26ad..12e0c3ffb 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -19,6 +19,8 @@ void KServerPort::Initialize(KPort* parent_port_, std::string&& name_) {
// Set member variables.
parent = parent_port_;
name = std::move(name_);
+
+ kernel.RegisterServerObject(this);
}
bool KServerPort::IsLight() const {
@@ -62,9 +64,6 @@ void KServerPort::Destroy() {
// Close our reference to our parent.
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);
}
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index fd4f4bd20..5fc7ee683 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -27,24 +27,6 @@ public:
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);
KServerSession* AcceptSession();
@@ -65,7 +47,6 @@ private:
void CleanupSessions();
SessionList session_list;
- SessionRequestHandlerWeakPtr session_handler;
KPort* parent{};
};
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index faf03fcc8..aa1941f01 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -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
#include <tuple>
@@ -33,12 +33,10 @@ KServerSession::KServerSession(KernelCore& kernel_)
KServerSession::~KServerSession() = default;
-void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
- std::shared_ptr<SessionRequestManager> manager_) {
+void KServerSession::Initialize(KSession* parent_session_, std::string&& name_) {
// Set member variables.
parent = parent_session_;
name = std::move(name_);
- manager = manager_;
}
void KServerSession::Destroy() {
@@ -47,18 +45,99 @@ void KServerSession::Destroy() {
this->CleanupRequests();
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() {
- if (manager && manager->HasSessionHandler()) {
- manager->SessionHandler().ClientDisconnected(this);
+ KScopedLightLock lk{m_lock};
+
+ // 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 {
@@ -73,24 +152,6 @@ bool KServerSession::IsSignaled() const {
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) {
// Create the wait queue.
ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
@@ -105,24 +166,16 @@ Result KServerSession::OnRequest(KSessionRequest* request) {
// Check that we're not terminating.
R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), ResultTerminationRequested);
- if (manager) {
- // HLE request.
- auto& memory{kernel.System().Memory()};
- this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
- } else {
- // Non-HLE request.
-
- // Get whether we're empty.
- const bool was_empty = m_request_list.empty();
+ // Get whether we're empty.
+ const bool was_empty = m_request_list.empty();
- // Add the request to the list.
- request->Open();
- m_request_list.push_back(*request);
+ // Add the request to the list.
+ request->Open();
+ m_request_list.push_back(*request);
- // If we were empty, signal.
- if (was_empty) {
- 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.
@@ -136,7 +189,7 @@ Result KServerSession::OnRequest(KSessionRequest* request) {
return GetCurrentThread(kernel).GetWaitResult();
}
-Result KServerSession::SendReply() {
+Result KServerSession::SendReply(bool is_hle) {
// Lock the session.
KScopedLightLock lk{m_lock};
@@ -171,13 +224,18 @@ Result KServerSession::SendReply() {
Result result = ResultSuccess;
if (!closed) {
// If we're not closed, send the reply.
- Core::Memory::Memory& memory{kernel.System().Memory()};
- KThread* server_thread{GetCurrentThreadPointer(kernel)};
- UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
+ if (is_hle) {
+ // HLE servers write directly to a pointer to the thread command buffer. Therefore
+ // 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* dst_msg_buffer = memory.GetPointer(client_message);
- std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
+ auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
+ auto* dst_msg_buffer = memory.GetPointer(client_message);
+ std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
+ }
} else {
result = ResultSessionClosed;
}
@@ -223,7 +281,8 @@ Result KServerSession::SendReply() {
return result;
}
-Result KServerSession::ReceiveRequest() {
+Result KServerSession::ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context,
+ std::weak_ptr<SessionRequestManager> manager) {
// Lock the session.
KScopedLightLock lk{m_lock};
@@ -267,12 +326,22 @@ Result KServerSession::ReceiveRequest() {
// Receive the message.
Core::Memory::Memory& memory{kernel.System().Memory()};
- KThread* server_thread{GetCurrentThreadPointer(kernel)};
- UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
+ if (out_context != nullptr) {
+ // 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* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
- std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
+ auto* src_msg_buffer = memory.GetPointer(client_message);
+ auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
+ std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
+ }
// We succeeded.
return ResultSuccess;
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 188aef4af..e4698d3f5 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -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
#pragma once
@@ -16,21 +16,11 @@
#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/result.h"
-namespace Core::Memory {
-class Memory;
-}
-
-namespace Core::Timing {
-class CoreTiming;
-struct EventType;
-} // namespace Core::Timing
-
namespace Kernel {
class HLERequestContext;
class KernelCore;
class KSession;
-class SessionRequestHandler;
class SessionRequestManager;
class KThread;
@@ -46,8 +36,7 @@ public:
void Destroy() override;
- void Initialize(KSession* parent_session_, std::string&& name_,
- std::shared_ptr<SessionRequestManager> manager_);
+ void Initialize(KSession* parent_session_, std::string&& name_);
KSession* GetParent() {
return parent;
@@ -60,32 +49,16 @@ public:
bool IsSignaled() const override;
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
Result OnRequest(KSessionRequest* request);
- Result SendReply();
- Result ReceiveRequest();
+ Result SendReply(bool is_hle = false);
+ Result ReceiveRequest(std::shared_ptr<HLERequestContext>* out_context = nullptr,
+ std::weak_ptr<SessionRequestManager> manager = {});
private:
/// Frees up waiting client sessions when this server session is about to die
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* parent{};
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index ee05aa282..7a6534ac3 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -13,8 +13,7 @@ KSession::KSession(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, server{kernel_}, client{kernel_} {}
KSession::~KSession() = default;
-void KSession::Initialize(KClientPort* port_, const std::string& name_,
- std::shared_ptr<SessionRequestManager> manager_) {
+void KSession::Initialize(KClientPort* port_, const std::string& name_) {
// Increment reference count.
// Because reference count is one on creation, this will result
// in a reference count of two. Thus, when both server and client are closed
@@ -26,7 +25,7 @@ void KSession::Initialize(KClientPort* port_, const std::string& name_,
KAutoObject::Create(std::addressof(client));
// Initialize our sub sessions.
- server.Initialize(this, name_ + ":Server", manager_);
+ server.Initialize(this, name_ + ":Server");
client.Initialize(this, name_ + ":Client");
// Set state and name.
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index c6ead403b..93e5e6f71 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/src/core/hle/kernel/k_session.h
@@ -21,8 +21,7 @@ public:
explicit KSession(KernelCore& kernel_);
~KSession() override;
- void Initialize(KClientPort* port_, const std::string& name_,
- std::shared_ptr<SessionRequestManager> manager_ = nullptr);
+ void Initialize(KClientPort* port_, const std::string& name_);
void Finalize() override;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index fdc774e30..29e122dfd 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -60,7 +60,6 @@ struct KernelCore::Impl {
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
global_handle_table->Initialize(KHandleTable::MaxTableSize);
- default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
is_phantom_mode_for_singlecore = false;
@@ -86,6 +85,8 @@ struct KernelCore::Impl {
}
RegisterHostThread();
+
+ default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
}
void InitializeCores() {
@@ -703,6 +704,15 @@ struct KernelCore::Impl {
return port;
}
+ void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
+ auto search = service_interface_handlers.find(name);
+ if (search == service_interface_handlers.end()) {
+ return;
+ }
+
+ search->second(system.ServiceManager(), server_port);
+ }
+
void RegisterServerObject(KAutoObject* server_object) {
std::scoped_lock lk(server_objects_lock);
server_objects.insert(server_object);
@@ -715,7 +725,7 @@ struct KernelCore::Impl {
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
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(
[this, service_thread]() { service_threads.emplace(service_thread); });
@@ -774,6 +784,7 @@ struct KernelCore::Impl {
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
+ std::unordered_map<std::string, ServiceInterfaceHandlerFn> service_interface_handlers;
NamedPortTable named_ports;
std::unordered_set<KAutoObject*> server_objects;
std::unordered_set<KAutoObject*> registered_objects;
@@ -981,10 +992,19 @@ void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&
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) {
return impl->CreateNamedServicePort(std::move(name));
}
+void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
+ impl->RegisterNamedServiceHandler(std::move(name), server_port);
+}
+
void KernelCore::RegisterServerObject(KAutoObject* server_object) {
impl->RegisterServerObject(server_object);
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 266be2bc4..670f93ee3 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -45,6 +45,7 @@ class KPort;
class KProcess;
class KResourceLimit;
class KScheduler;
+class KServerPort;
class KServerSession;
class KSession;
class KSessionRequest;
@@ -63,6 +64,8 @@ class TimeManager;
using ServiceInterfaceFactory =
std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
+using ServiceInterfaceHandlerFn = std::function<void(Service::SM::ServiceManager&, KServerPort*)>;
+
namespace Init {
struct KSlabResourceCounts;
}
@@ -192,9 +195,15 @@ public:
/// Registers a named HLE service, passing a factory used to open a port to that service.
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.
KClientPort* CreateNamedServicePort(std::string name);
+ /// Accepts a session on a port created by CreateNamedServicePort.
+ void RegisterNamedServiceHandler(std::string name, KServerPort* server_port);
+
/// Registers a server session or port with the gobal emulation state, to be freed on shutdown.
/// This is necessary because we do not emulate processes for HLE sessions and ports.
void RegisterServerObject(KAutoObject* server_object);
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index d23d76706..1fc2edf52 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -1,15 +1,17 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
-#include <queue>
#include "common/scope_exit.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_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -19,101 +21,201 @@ namespace Kernel {
class ServiceThread::Impl final {
public:
- explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
+ explicit Impl(KernelCore& kernel, const std::string& service_name);
~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:
- std::vector<std::jthread> threads;
- std::queue<std::function<void()>> requests;
- std::mutex queue_mutex;
- std::condition_variable_any condition;
- const std::string service_name;
+ KernelCore& kernel;
+
+ std::jthread m_thread;
+ std::mutex m_session_mutex;
+ std::vector<KServerSession*> m_sessions;
+ std::vector<std::shared_ptr<SessionRequestManager>> m_managers;
+ 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)
- : service_name{name} {
- for (std::size_t i = 0; i < num_threads; ++i) {
- threads.emplace_back([this, &kernel](std::stop_token stop_token) {
- Common::SetCurrentThreadName(std::string{service_name}.c_str());
+void ServiceThread::Impl::WaitAndProcessImpl() {
+ // Create local list of waitable sessions.
+ std::vector<KSynchronizationObject*> objs;
+ std::vector<std::shared_ptr<SessionRequestManager>> managers;
- // Wait for first request before trying to acquire a render context
- {
- std::unique_lock lock{queue_mutex};
- condition.wait(lock, stop_token, [this] { return !requests.empty(); });
- }
+ {
+ // Lock to get the list.
+ std::scoped_lock lk{m_session_mutex};
- if (stop_token.stop_requested()) {
- return;
- }
+ // Resize to the needed quantity.
+ objs.resize(m_sessions.size() + 1);
+ managers.resize(m_managers.size());
- // Allocate a dummy guest thread for this host thread.
- kernel.RegisterHostThread();
+ // Copy to our local list.
+ std::copy(m_sessions.begin(), m_sessions.end(), objs.begin());
+ std::copy(m_managers.begin(), m_managers.end(), managers.begin());
- while (true) {
- std::function<void()> task;
+ // Insert the wakeup event at the end.
+ objs.back() = &m_wakeup_event->GetReadableEvent();
+ }
- {
- std::unique_lock lock{queue_mutex};
- condition.wait(lock, stop_token, [this] { return !requests.empty(); });
+ // Wait on the list of sessions.
+ s32 index{-1};
+ Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(),
+ static_cast<s32>(objs.size()), -1);
+ ASSERT(!rc.IsFailure());
+
+ // If this was the wakeup event, clear it and finish.
+ if (index >= static_cast<s64>(objs.size() - 1)) {
+ m_wakeup_event->Clear();
+ return;
+ }
- if (stop_token.stop_requested()) {
- return;
- }
+ // This event is from a server session.
+ auto* server_session = static_cast<KServerSession*>(objs[index]);
+ auto& manager = managers[index];
- if (requests.empty()) {
- continue;
- }
+ // Fetch the HLE request context.
+ std::shared_ptr<HLERequestContext> context;
+ rc = server_session->ReceiveRequest(&context, manager);
- task = std::move(requests.front());
- requests.pop();
- }
+ // If the session was closed, handle that.
+ if (rc == ResultSessionClosed) {
+ SessionClosed(server_session, manager);
- task();
- }
- });
+ // 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->SendReply(true);
+
+ 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::QueueSyncRequest(KSession& session,
- std::shared_ptr<HLERequestContext>&& context) {
+void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
+ std::shared_ptr<SessionRequestManager> manager) {
{
- std::unique_lock lock{queue_mutex};
+ // Lock to get the list.
+ std::scoped_lock lk{m_session_mutex};
+
+ // Get the index of the session.
+ const auto index =
+ std::find(m_sessions.begin(), m_sessions.end(), server_session) - m_sessions.begin();
+ ASSERT(index < static_cast<s64>(m_sessions.size()));
+
+ // Remove the session and its manager.
+ m_sessions.erase(m_sessions.begin() + index);
+ m_managers.erase(m_managers.begin() + index);
+ }
- auto* server_session{&session.GetServerSession()};
+ // Close our reference to the server session.
+ server_session->Close();
+}
- // Open a reference to the session to ensure it is not closes while the service request
- // completes asynchronously.
- server_session->Open();
+void ServiceThread::Impl::LoopProcess() {
+ Common::SetCurrentThreadName(m_service_name.c_str());
- requests.emplace([server_session, context{std::move(context)}]() {
- // Close the reference.
- SCOPE_EXIT({ server_session->Close(); });
+ kernel.RegisterHostThread();
- // Complete the service request.
- server_session->CompleteSyncRequest(*context);
- });
+ while (!m_shutdown_requested.load()) {
+ WaitAndProcessImpl();
}
- condition.notify_one();
+}
+
+void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session,
+ std::shared_ptr<SessionRequestManager> manager) {
+ // Open the server session.
+ server_session->Open();
+
+ {
+ // Lock to get the list.
+ std::scoped_lock lk{m_session_mutex};
+
+ // Insert the session and manager.
+ m_sessions.push_back(server_session);
+ m_managers.push_back(manager);
+ }
+
+ // Signal the wakeup event.
+ m_wakeup_event->Signal();
}
ServiceThread::Impl::~Impl() {
- condition.notify_all();
- for (auto& thread : threads) {
- thread.request_stop();
- thread.join();
+ // Shut down the processing thread.
+ m_shutdown_requested.store(true);
+ m_wakeup_event->Signal();
+ m_thread.join();
+
+ // Lock mutex.
+ m_session_mutex.lock();
+
+ // Close all remaining sessions.
+ for (size_t i = 0; i < m_sessions.size(); i++) {
+ m_sessions[i]->Close();
}
+
+ // Close event.
+ m_wakeup_event->GetReadableEvent().Close();
+ m_wakeup_event->Close();
+
+ // Close process.
+ m_process->Close();
+}
+
+ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_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, std::size_t num_threads, const std::string& name)
- : impl{std::make_unique<Impl>(kernel, num_threads, name)} {}
+ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)
+ : impl{std::make_unique<Impl>(kernel, name)} {}
ServiceThread::~ServiceThread() = default;
-void ServiceThread::QueueSyncRequest(KSession& session,
- std::shared_ptr<HLERequestContext>&& context) {
- impl->QueueSyncRequest(session, std::move(context));
+void ServiceThread::RegisterServerSession(KServerSession* session,
+ std::shared_ptr<SessionRequestManager> manager) {
+ impl->RegisterServerSession(session, manager);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
index c5896f2bd..fb4325531 100644
--- a/src/core/hle/kernel/service_thread.h
+++ b/src/core/hle/kernel/service_thread.h
@@ -11,13 +11,15 @@ namespace Kernel {
class HLERequestContext;
class KernelCore;
class KSession;
+class SessionRequestManager;
class ServiceThread final {
public:
- explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name);
+ explicit ServiceThread(KernelCore& kernel, const std::string& name);
~ServiceThread();
- void QueueSyncRequest(KSession& session, std::shared_ptr<HLERequestContext>&& context);
+ void RegisterServerSession(KServerSession* session,
+ std::shared_ptr<SessionRequestManager> manager);
private:
class Impl;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4aca5b27d..8d2c7d6b7 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -24,6 +24,7 @@
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_layout.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_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -382,10 +383,11 @@ static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_n
// Create a session.
KClientSession* session{};
- R_TRY(port->CreateSession(std::addressof(session),
- std::make_shared<SessionRequestManager>(kernel)));
+ R_TRY(port->CreateSession(std::addressof(session)));
port->Close();
+ kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
+
// Register the session in the table, close the extra reference.
handle_table.Register(*out, session);
session->Close();