|
@@ -7,6 +7,8 @@
|
|
|
#include "common/assert.h"
|
|
#include "common/assert.h"
|
|
|
#include "common/common_types.h"
|
|
#include "common/common_types.h"
|
|
|
#include "common/logging/log.h"
|
|
#include "common/logging/log.h"
|
|
|
|
|
+#include "common/scope_exit.h"
|
|
|
|
|
+#include "core/core.h"
|
|
|
#include "core/core_timing.h"
|
|
#include "core/core_timing.h"
|
|
|
#include "core/hle/ipc_helpers.h"
|
|
#include "core/hle/ipc_helpers.h"
|
|
|
#include "core/hle/kernel/hle_ipc.h"
|
|
#include "core/hle/kernel/hle_ipc.h"
|
|
@@ -18,13 +20,19 @@
|
|
|
#include "core/hle/kernel/k_server_session.h"
|
|
#include "core/hle/kernel/k_server_session.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/k_thread_queue.h"
|
|
|
#include "core/hle/kernel/kernel.h"
|
|
#include "core/hle/kernel/kernel.h"
|
|
|
#include "core/hle/kernel/service_thread.h"
|
|
#include "core/hle/kernel/service_thread.h"
|
|
|
#include "core/memory.h"
|
|
#include "core/memory.h"
|
|
|
|
|
|
|
|
namespace Kernel {
|
|
namespace Kernel {
|
|
|
|
|
|
|
|
-KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
|
|
|
|
|
|
|
+using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
|
|
|
|
|
+
|
|
|
|
|
+static constexpr u32 MessageBufferSize = 0x100;
|
|
|
|
|
+
|
|
|
|
|
+KServerSession::KServerSession(KernelCore& kernel_)
|
|
|
|
|
+ : KSynchronizationObject{kernel_}, m_lock{kernel_} {}
|
|
|
|
|
|
|
|
KServerSession::~KServerSession() = default;
|
|
KServerSession::~KServerSession() = default;
|
|
|
|
|
|
|
@@ -33,17 +41,14 @@ void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
|
|
|
// Set member variables.
|
|
// Set member variables.
|
|
|
parent = parent_session_;
|
|
parent = parent_session_;
|
|
|
name = std::move(name_);
|
|
name = std::move(name_);
|
|
|
-
|
|
|
|
|
- if (manager_) {
|
|
|
|
|
- manager = manager_;
|
|
|
|
|
- } else {
|
|
|
|
|
- manager = std::make_shared<SessionRequestManager>(kernel);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ manager = manager_;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void KServerSession::Destroy() {
|
|
void KServerSession::Destroy() {
|
|
|
parent->OnServerClosed();
|
|
parent->OnServerClosed();
|
|
|
|
|
|
|
|
|
|
+ this->CleanupRequests();
|
|
|
|
|
+
|
|
|
parent->Close();
|
|
parent->Close();
|
|
|
|
|
|
|
|
// Release host emulation members.
|
|
// Release host emulation members.
|
|
@@ -54,13 +59,13 @@ void KServerSession::Destroy() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void KServerSession::OnClientClosed() {
|
|
void KServerSession::OnClientClosed() {
|
|
|
- if (manager->HasSessionHandler()) {
|
|
|
|
|
|
|
+ if (manager && manager->HasSessionHandler()) {
|
|
|
manager->SessionHandler().ClientDisconnected(this);
|
|
manager->SessionHandler().ClientDisconnected(this);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool KServerSession::IsSignaled() const {
|
|
bool KServerSession::IsSignaled() const {
|
|
|
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
|
|
|
|
|
|
|
+ ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel));
|
|
|
|
|
|
|
|
// If the client is closed, we're always signaled.
|
|
// If the client is closed, we're always signaled.
|
|
|
if (parent->IsClientClosed()) {
|
|
if (parent->IsClientClosed()) {
|
|
@@ -68,7 +73,7 @@ bool KServerSession::IsSignaled() const {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, we're signaled if we have a request and aren't handling one.
|
|
// Otherwise, we're signaled if we have a request and aren't handling one.
|
|
|
- return false;
|
|
|
|
|
|
|
+ return !m_thread_request_list.empty() && m_current_thread_request == nullptr;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) {
|
|
void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) {
|
|
@@ -173,9 +178,221 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
|
|
|
|
|
- Core::Timing::CoreTiming& core_timing) {
|
|
|
|
|
- return QueueSyncRequest(thread, memory);
|
|
|
|
|
|
|
+Result KServerSession::OnRequest() {
|
|
|
|
|
+ // Create the wait queue.
|
|
|
|
|
+ ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ // Lock the scheduler.
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ // Ensure that we can handle new requests.
|
|
|
|
|
+ R_UNLESS(!parent->IsServerClosed(), ResultSessionClosed);
|
|
|
|
|
+
|
|
|
|
|
+ // 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.
|
|
|
|
|
+ auto* thread{GetCurrentThreadPointer(kernel)};
|
|
|
|
|
+
|
|
|
|
|
+ // Get whether we're empty.
|
|
|
|
|
+ const bool was_empty = m_thread_request_list.empty();
|
|
|
|
|
+
|
|
|
|
|
+ // Add the thread to the list.
|
|
|
|
|
+ thread->Open();
|
|
|
|
|
+ m_thread_request_list.push_back(thread);
|
|
|
|
|
+
|
|
|
|
|
+ // If we were empty, signal.
|
|
|
|
|
+ if (was_empty) {
|
|
|
|
|
+ this->NotifyAvailable();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // This is a synchronous request, so we should wait for our request to complete.
|
|
|
|
|
+ GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
|
|
|
|
|
+ GetCurrentThread(kernel).BeginWait(&wait_queue);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return GetCurrentThread(kernel).GetWaitResult();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Result KServerSession::SendReply() {
|
|
|
|
|
+ // Lock the session.
|
|
|
|
|
+ KScopedLightLock lk(m_lock);
|
|
|
|
|
+
|
|
|
|
|
+ // Get the request.
|
|
|
|
|
+ KThread* client_thread;
|
|
|
|
|
+ {
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ // Get the current request.
|
|
|
|
|
+ client_thread = m_current_thread_request;
|
|
|
|
|
+ R_UNLESS(client_thread != nullptr, ResultInvalidState);
|
|
|
|
|
+
|
|
|
|
|
+ // Clear the current request, since we're processing it.
|
|
|
|
|
+ m_current_thread_request = nullptr;
|
|
|
|
|
+ if (!m_thread_request_list.empty()) {
|
|
|
|
|
+ this->NotifyAvailable();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Close reference to the request once we're done processing it.
|
|
|
|
|
+ SCOPE_EXIT({ client_thread->Close(); });
|
|
|
|
|
+
|
|
|
|
|
+ // Extract relevant information from the request.
|
|
|
|
|
+ // const uintptr_t client_message = request->GetAddress();
|
|
|
|
|
+ // const size_t client_buffer_size = request->GetSize();
|
|
|
|
|
+ // KThread *client_thread = request->GetThread();
|
|
|
|
|
+ // KEvent *event = request->GetEvent();
|
|
|
|
|
+
|
|
|
|
|
+ // Check whether we're closed.
|
|
|
|
|
+ const bool closed = (client_thread == nullptr || parent->IsClientClosed());
|
|
|
|
|
+
|
|
|
|
|
+ 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());
|
|
|
|
|
+
|
|
|
|
|
+ auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
|
|
|
|
+ auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress());
|
|
|
|
|
+ std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ result = ResultSessionClosed;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Select a result for the client.
|
|
|
|
|
+ Result client_result = result;
|
|
|
|
|
+ if (closed && R_SUCCEEDED(result)) {
|
|
|
|
|
+ result = ResultSessionClosed;
|
|
|
|
|
+ client_result = ResultSessionClosed;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ result = ResultSuccess;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If there's a client thread, update it.
|
|
|
|
|
+ if (client_thread != nullptr) {
|
|
|
|
|
+ // End the client thread's wait.
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ if (!client_thread->IsTerminationRequested()) {
|
|
|
|
|
+ client_thread->EndWait(client_result);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Result KServerSession::ReceiveRequest() {
|
|
|
|
|
+ // Lock the session.
|
|
|
|
|
+ KScopedLightLock lk(m_lock);
|
|
|
|
|
+
|
|
|
|
|
+ // Get the request and client thread.
|
|
|
|
|
+ // KSessionRequest *request;
|
|
|
|
|
+ KThread* client_thread;
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ // Ensure that we can service the request.
|
|
|
|
|
+ R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
|
|
|
|
|
+
|
|
|
|
|
+ // Ensure we aren't already servicing a request.
|
|
|
|
|
+ R_UNLESS(m_current_thread_request == nullptr, ResultNotFound);
|
|
|
|
|
+
|
|
|
|
|
+ // Ensure we have a request to service.
|
|
|
|
|
+ R_UNLESS(!m_thread_request_list.empty(), ResultNotFound);
|
|
|
|
|
+
|
|
|
|
|
+ // Pop the first request from the list.
|
|
|
|
|
+ client_thread = m_thread_request_list.front();
|
|
|
|
|
+ m_thread_request_list.pop_front();
|
|
|
|
|
+
|
|
|
|
|
+ // Get the thread for the request.
|
|
|
|
|
+ R_UNLESS(client_thread != nullptr, ResultSessionClosed);
|
|
|
|
|
+
|
|
|
|
|
+ // Open the client thread.
|
|
|
|
|
+ client_thread->Open();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // SCOPE_EXIT({ client_thread->Close(); });
|
|
|
|
|
+
|
|
|
|
|
+ // Set the request as our current.
|
|
|
|
|
+ m_current_thread_request = client_thread;
|
|
|
|
|
+
|
|
|
|
|
+ // Receive the message.
|
|
|
|
|
+ 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(client_thread->GetTLSAddress());
|
|
|
|
|
+ auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
|
|
|
|
|
+ std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize);
|
|
|
|
|
+
|
|
|
|
|
+ // We succeeded.
|
|
|
|
|
+ return ResultSuccess;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void KServerSession::CleanupRequests() {
|
|
|
|
|
+ KScopedLightLock lk(m_lock);
|
|
|
|
|
+
|
|
|
|
|
+ // Clean up any pending requests.
|
|
|
|
|
+ while (true) {
|
|
|
|
|
+ // Get the next request.
|
|
|
|
|
+ // KSessionRequest *request = nullptr;
|
|
|
|
|
+ KThread* client_thread = nullptr;
|
|
|
|
|
+ {
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ if (m_current_thread_request) {
|
|
|
|
|
+ // Choose the current request if we have one.
|
|
|
|
|
+ client_thread = m_current_thread_request;
|
|
|
|
|
+ m_current_thread_request = nullptr;
|
|
|
|
|
+ } else if (!m_thread_request_list.empty()) {
|
|
|
|
|
+ // Pop the request from the front of the list.
|
|
|
|
|
+ client_thread = m_thread_request_list.front();
|
|
|
|
|
+ m_thread_request_list.pop_front();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // If there's no request, we're done.
|
|
|
|
|
+ if (client_thread == nullptr) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Close a reference to the request once it's cleaned up.
|
|
|
|
|
+ SCOPE_EXIT({ client_thread->Close(); });
|
|
|
|
|
+
|
|
|
|
|
+ // Extract relevant information from the request.
|
|
|
|
|
+ // const uintptr_t client_message = request->GetAddress();
|
|
|
|
|
+ // const size_t client_buffer_size = request->GetSize();
|
|
|
|
|
+ // KThread *client_thread = request->GetThread();
|
|
|
|
|
+ // KEvent *event = request->GetEvent();
|
|
|
|
|
+
|
|
|
|
|
+ // KProcess *server_process = request->GetServerProcess();
|
|
|
|
|
+ // KProcess *client_process = (client_thread != nullptr) ?
|
|
|
|
|
+ // client_thread->GetOwnerProcess() : nullptr;
|
|
|
|
|
+ // KProcessPageTable *client_page_table = (client_process != nullptr) ?
|
|
|
|
|
+ // &client_process->GetPageTable() : nullptr;
|
|
|
|
|
+
|
|
|
|
|
+ // Cleanup the mappings.
|
|
|
|
|
+ // Result result = CleanupMap(request, server_process, client_page_table);
|
|
|
|
|
+
|
|
|
|
|
+ // If there's a client thread, update it.
|
|
|
|
|
+ if (client_thread != nullptr) {
|
|
|
|
|
+ // End the client thread's wait.
|
|
|
|
|
+ KScopedSchedulerLock sl{kernel};
|
|
|
|
|
+
|
|
|
|
|
+ if (!client_thread->IsTerminationRequested()) {
|
|
|
|
|
+ client_thread->EndWait(ResultSessionClosed);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
} // namespace Kernel
|
|
} // namespace Kernel
|