|
@@ -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_,
|
|
|
|
|
- std::shared_ptr<SessionRequestManager> manager_) {
|
|
|
|
|
|
|
+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_);
|
|
|
- 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()) {
|
|
|
|
|
- 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 {
|
|
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) {
|
|
|
|
|
- // 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.
|
|
// 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()};
|
|
|
|
|
- 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 {
|
|
} 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)};
|
|
|
|
|
- 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.
|
|
// We succeeded.
|
|
|
return ResultSuccess;
|
|
return ResultSuccess;
|