|
|
|
@ -7,6 +7,8 @@ |
|
|
|
#include "common/assert.h"
|
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "common/logging/log.h"
|
|
|
|
#include "common/scope_exit.h"
|
|
|
|
#include "core/core.h"
|
|
|
|
#include "core/core_timing.h"
|
|
|
|
#include "core/hle/ipc_helpers.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_session.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/service_thread.h"
|
|
|
|
#include "core/memory.h"
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
@ -33,17 +41,14 @@ void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, |
|
|
|
// Set member variables.
|
|
|
|
parent = parent_session_; |
|
|
|
name = std::move(name_); |
|
|
|
|
|
|
|
if (manager_) { |
|
|
|
manager = manager_; |
|
|
|
} else { |
|
|
|
manager = std::make_shared<SessionRequestManager>(kernel); |
|
|
|
} |
|
|
|
manager = manager_; |
|
|
|
} |
|
|
|
|
|
|
|
void KServerSession::Destroy() { |
|
|
|
parent->OnServerClosed(); |
|
|
|
|
|
|
|
this->CleanupRequests(); |
|
|
|
|
|
|
|
parent->Close(); |
|
|
|
|
|
|
|
// Release host emulation members.
|
|
|
|
@ -54,13 +59,13 @@ void KServerSession::Destroy() { |
|
|
|
} |
|
|
|
|
|
|
|
void KServerSession::OnClientClosed() { |
|
|
|
if (manager->HasSessionHandler()) { |
|
|
|
if (manager && manager->HasSessionHandler()) { |
|
|
|
manager->SessionHandler().ClientDisconnected(this); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool KServerSession::IsSignaled() const { |
|
|
|
ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
|
|
|
ASSERT(KScheduler::IsSchedulerLockedByCurrentThread(kernel)); |
|
|
|
|
|
|
|
// If the client is closed, we're always signaled.
|
|
|
|
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.
|
|
|
|
return false; |
|
|
|
return !m_thread_request_list.empty() && m_current_thread_request == nullptr; |
|
|
|
} |
|
|
|
|
|
|
|
void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) { |
|
|
|
@ -173,9 +178,221 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { |
|
|
|
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
|