summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/kernel/k_client_port.cpp69
-rw-r--r--src/core/hle/kernel/k_client_port.h2
-rw-r--r--src/core/hle/kernel/k_client_session.cpp24
-rw-r--r--src/core/hle/kernel/k_client_session.h18
-rw-r--r--src/core/hle/kernel/k_light_client_session.cpp31
-rw-r--r--src/core/hle/kernel/k_light_client_session.h39
-rw-r--r--src/core/hle/kernel/k_light_server_session.cpp247
-rw-r--r--src/core/hle/kernel/k_light_server_session.h49
-rw-r--r--src/core/hle/kernel/k_light_session.cpp81
-rw-r--r--src/core/hle/kernel/k_light_session.h86
-rw-r--r--src/core/hle/kernel/k_port.cpp9
-rw-r--r--src/core/hle/kernel/k_port.h2
-rw-r--r--src/core/hle/kernel/k_server_port.cpp56
-rw-r--r--src/core/hle/kernel/k_server_port.h5
-rw-r--r--src/core/hle/kernel/k_server_session.cpp5
-rw-r--r--src/core/hle/kernel/k_session.h4
-rw-r--r--src/core/hle/kernel/k_thread.h7
-rw-r--r--src/core/hle/kernel/kernel.cpp4
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp296
-rw-r--r--src/core/hle/kernel/svc/svc_light_ipc.cpp29
-rw-r--r--src/core/hle/kernel/svc/svc_port.cpp71
-rw-r--r--src/core/hle/kernel/svc/svc_session.cpp40
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp1
-rw-r--r--src/core/hle/service/sm/sm.cpp2
24 files changed, 1052 insertions, 125 deletions
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 40e09e532..11b1b977e 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -3,6 +3,7 @@
#include "common/scope_exit.h"
#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_light_session.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
@@ -63,6 +64,7 @@ Result KClientPort::CreateSession(KClientSession** out) {
R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
// Allocate a session normally.
+ // TODO: Dynamic resource limits
session = KSession::Create(m_kernel);
// Check that we successfully created a session.
@@ -119,4 +121,71 @@ Result KClientPort::CreateSession(KClientSession** out) {
R_SUCCEED();
}
+Result KClientPort::CreateLightSession(KLightClientSession** out) {
+ // Declare the session we're going to allocate.
+ KLightSession* session{};
+
+ // Reserve a new session from the resource limit.
+ KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
+ Svc::LimitableResource::SessionCountMax);
+ R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
+
+ // Allocate a session normally.
+ // TODO: Dynamic resource limits
+ session = KLightSession::Create(m_kernel);
+
+ // Check that we successfully created a session.
+ R_UNLESS(session != nullptr, ResultOutOfResource);
+
+ // Update the session counts.
+ {
+ ON_RESULT_FAILURE {
+ session->Close();
+ };
+
+ // Atomically increment the number of sessions.
+ s32 new_sessions;
+ {
+ const auto max = m_max_sessions;
+ auto cur_sessions = m_num_sessions.load(std::memory_order_acquire);
+ do {
+ R_UNLESS(cur_sessions < max, ResultOutOfSessions);
+ new_sessions = cur_sessions + 1;
+ } while (!m_num_sessions.compare_exchange_weak(cur_sessions, new_sessions,
+ std::memory_order_relaxed));
+ }
+
+ // Atomically update the peak session tracking.
+ {
+ auto peak = m_peak_sessions.load(std::memory_order_acquire);
+ do {
+ if (peak >= new_sessions) {
+ break;
+ }
+ } while (!m_peak_sessions.compare_exchange_weak(peak, new_sessions,
+ std::memory_order_relaxed));
+ }
+ }
+
+ // Initialize the session.
+ session->Initialize(this, m_parent->GetName());
+
+ // Commit the session reservation.
+ session_reservation.Commit();
+
+ // Register the session.
+ KLightSession::Register(m_kernel, session);
+ ON_RESULT_FAILURE {
+ session->GetClientSession().Close();
+ session->GetServerSession().Close();
+ };
+
+ // Enqueue the session with our parent.
+ R_TRY(m_parent->EnqueueSession(std::addressof(session->GetServerSession())));
+
+ // We succeeded, so set the output.
+ *out = std::addressof(session->GetClientSession());
+ R_SUCCEED();
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index 23db06ddf..28b332608 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -11,6 +11,7 @@
namespace Kernel {
+class KLightClientSession;
class KClientSession;
class KernelCore;
class KPort;
@@ -51,6 +52,7 @@ public:
bool IsSignaled() const override;
Result CreateSession(KClientSession** out);
+ Result CreateLightSession(KLightClientSession** out);
private:
std::atomic<s32> m_num_sessions{};
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index 72b66270d..472e8571c 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -10,9 +10,7 @@
namespace Kernel {
-static constexpr u32 MessageBufferSize = 0x100;
-
-KClientSession::KClientSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
+KClientSession::KClientSession(KernelCore& kernel) : KAutoObject{kernel} {}
KClientSession::~KClientSession() = default;
void KClientSession::Destroy() {
@@ -22,18 +20,30 @@ void KClientSession::Destroy() {
void KClientSession::OnServerClosed() {}
-Result KClientSession::SendSyncRequest() {
+Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
+ // Create a session request.
+ KSessionRequest* request = KSessionRequest::Create(m_kernel);
+ R_UNLESS(request != nullptr, ResultOutOfResource);
+ SCOPE_EXIT({ request->Close(); });
+
+ // Initialize the request.
+ request->Initialize(nullptr, address, size);
+
+ // Send the request.
+ R_RETURN(m_parent->OnRequest(request));
+}
+
+Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t size) {
// Create a session request.
KSessionRequest* request = KSessionRequest::Create(m_kernel);
R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); });
// Initialize the request.
- request->Initialize(nullptr, GetInteger(GetCurrentThread(m_kernel).GetTlsAddress()),
- MessageBufferSize);
+ request->Initialize(event, address, size);
// Send the request.
- R_RETURN(m_parent->GetServerSession().OnRequest(request));
+ R_RETURN(m_parent->OnRequest(request));
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
index 9b62e55e4..a39213e17 100644
--- a/src/core/hle/kernel/k_client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -9,24 +9,12 @@
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
-union Result;
-
-namespace Core::Memory {
-class Memory;
-}
-
-namespace Core::Timing {
-class CoreTiming;
-}
-
namespace Kernel {
class KernelCore;
class KSession;
-class KThread;
-class KClientSession final
- : public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
+class KClientSession final : public KAutoObject {
KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
public:
@@ -39,13 +27,13 @@ public:
}
void Destroy() override;
- static void PostDestroy(uintptr_t arg) {}
KSession* GetParent() const {
return m_parent;
}
- Result SendSyncRequest();
+ Result SendSyncRequest(uintptr_t address, size_t size);
+ Result SendAsyncRequest(KEvent* event, uintptr_t address, size_t size);
void OnServerClosed();
diff --git a/src/core/hle/kernel/k_light_client_session.cpp b/src/core/hle/kernel/k_light_client_session.cpp
new file mode 100644
index 000000000..8ce3e1ae4
--- /dev/null
+++ b/src/core/hle/kernel/k_light_client_session.cpp
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_light_client_session.h"
+#include "core/hle/kernel/k_light_session.h"
+#include "core/hle/kernel/k_thread.h"
+
+namespace Kernel {
+
+KLightClientSession::KLightClientSession(KernelCore& kernel) : KAutoObject(kernel) {}
+
+KLightClientSession::~KLightClientSession() = default;
+
+void KLightClientSession::Destroy() {
+ m_parent->OnClientClosed();
+}
+
+void KLightClientSession::OnServerClosed() {}
+
+Result KLightClientSession::SendSyncRequest(u32* data) {
+ // Get the request thread.
+ KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
+
+ // Set the light data.
+ cur_thread->SetLightSessionData(data);
+
+ // Send the request.
+ R_RETURN(m_parent->OnRequest(cur_thread));
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_client_session.h b/src/core/hle/kernel/k_light_client_session.h
new file mode 100644
index 000000000..881a15cbd
--- /dev/null
+++ b/src/core/hle/kernel/k_light_client_session.h
@@ -0,0 +1,39 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KLightSession;
+
+class KLightClientSession final : public KAutoObject {
+ KERNEL_AUTOOBJECT_TRAITS(KLightClientSession, KAutoObject);
+
+public:
+ explicit KLightClientSession(KernelCore& kernel);
+ ~KLightClientSession();
+
+ void Initialize(KLightSession* parent) {
+ // Set member variables.
+ m_parent = parent;
+ }
+
+ virtual void Destroy() override;
+
+ const KLightSession* GetParent() const {
+ return m_parent;
+ }
+
+ Result SendSyncRequest(u32* data);
+
+ void OnServerClosed();
+
+private:
+ KLightSession* m_parent;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_server_session.cpp b/src/core/hle/kernel/k_light_server_session.cpp
new file mode 100644
index 000000000..e5ceb01f2
--- /dev/null
+++ b/src/core/hle/kernel/k_light_server_session.cpp
@@ -0,0 +1,247 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_light_server_session.h"
+#include "core/hle/kernel/k_light_session.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_queue.h"
+#include "core/hle/kernel/svc_results.h"
+
+namespace Kernel {
+
+namespace {
+
+constexpr u64 InvalidThreadId = std::numeric_limits<u64>::max();
+
+class ThreadQueueImplForKLightServerSessionRequest final : public KThreadQueue {
+private:
+ KThread::WaiterList* m_wait_list;
+
+public:
+ ThreadQueueImplForKLightServerSessionRequest(KernelCore& kernel, KThread::WaiterList* wl)
+ : KThreadQueue(kernel), m_wait_list(wl) {}
+
+ virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
+ // Remove the thread from our wait list.
+ m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
+
+ // Invoke the base end wait handler.
+ KThreadQueue::EndWait(waiting_thread, wait_result);
+ }
+
+ virtual void CancelWait(KThread* waiting_thread, Result wait_result,
+ bool cancel_timer_task) override {
+ // Remove the thread from our wait list.
+ m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+};
+
+class ThreadQueueImplForKLightServerSessionReceive final : public KThreadQueue {
+private:
+ KThread** m_server_thread;
+
+public:
+ ThreadQueueImplForKLightServerSessionReceive(KernelCore& kernel, KThread** st)
+ : KThreadQueue(kernel), m_server_thread(st) {}
+
+ virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
+ // Clear the server thread.
+ *m_server_thread = nullptr;
+
+ // Set the waiting thread as not cancelable.
+ waiting_thread->ClearCancellable();
+
+ // Invoke the base end wait handler.
+ KThreadQueue::EndWait(waiting_thread, wait_result);
+ }
+
+ virtual void CancelWait(KThread* waiting_thread, Result wait_result,
+ bool cancel_timer_task) override {
+ // Clear the server thread.
+ *m_server_thread = nullptr;
+
+ // Set the waiting thread as not cancelable.
+ waiting_thread->ClearCancellable();
+
+ // Invoke the base cancel wait handler.
+ KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
+ }
+};
+
+} // namespace
+
+KLightServerSession::KLightServerSession(KernelCore& kernel) : KAutoObject(kernel) {}
+KLightServerSession::~KLightServerSession() = default;
+
+void KLightServerSession::Destroy() {
+ this->CleanupRequests();
+
+ m_parent->OnServerClosed();
+}
+
+void KLightServerSession::OnClientClosed() {
+ this->CleanupRequests();
+}
+
+Result KLightServerSession::OnRequest(KThread* request_thread) {
+ ThreadQueueImplForKLightServerSessionRequest wait_queue(m_kernel,
+ std::addressof(m_request_list));
+
+ // Send the request.
+ {
+ // Lock the scheduler.
+ KScopedSchedulerLock sl(m_kernel);
+
+ // Check that the server isn't closed.
+ R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
+
+ // Check that the request thread isn't terminating.
+ R_UNLESS(!request_thread->IsTerminationRequested(), ResultTerminationRequested);
+
+ // Add the request thread to our list.
+ m_request_list.push_back(*request_thread);
+
+ // Begin waiting on the request.
+ request_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
+ request_thread->BeginWait(std::addressof(wait_queue));
+
+ // If we have a server thread, end its wait.
+ if (m_server_thread != nullptr) {
+ m_server_thread->EndWait(ResultSuccess);
+ }
+ }
+
+ // NOTE: Nintendo returns GetCurrentThread().GetWaitResult() here.
+ // This is technically incorrect, although it doesn't cause problems in practice
+ // because this is only ever called with request_thread = GetCurrentThreadPointer().
+ R_RETURN(request_thread->GetWaitResult());
+}
+
+Result KLightServerSession::ReplyAndReceive(u32* data) {
+ // Set the server context.
+ GetCurrentThread(m_kernel).SetLightSessionData(data);
+
+ // Reply, if we need to.
+ if (data[0] & KLightSession::ReplyFlag) {
+ KScopedSchedulerLock sl(m_kernel);
+
+ // Check that we're open.
+ R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
+ R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
+
+ // Check that we have a request to reply to.
+ R_UNLESS(m_current_request != nullptr, ResultInvalidState);
+
+ // Check that the server thread id is correct.
+ R_UNLESS(m_server_thread_id == GetCurrentThread(m_kernel).GetId(), ResultInvalidState);
+
+ // If we can reply, do so.
+ if (!m_current_request->IsTerminationRequested()) {
+ std::memcpy(m_current_request->GetLightSessionData(),
+ GetCurrentThread(m_kernel).GetLightSessionData(), KLightSession::DataSize);
+ m_current_request->EndWait(ResultSuccess);
+ }
+
+ // Close our current request.
+ m_current_request->Close();
+
+ // Clear our current request.
+ m_current_request = nullptr;
+ m_server_thread_id = InvalidThreadId;
+ }
+
+ // Create the wait queue for our receive.
+ ThreadQueueImplForKLightServerSessionReceive wait_queue(m_kernel,
+ std::addressof(m_server_thread));
+
+ // Receive.
+ while (true) {
+ // Try to receive a request.
+ {
+ KScopedSchedulerLock sl(m_kernel);
+
+ // Check that we aren't already receiving.
+ R_UNLESS(m_server_thread == nullptr, ResultInvalidState);
+ R_UNLESS(m_server_thread_id == InvalidThreadId, ResultInvalidState);
+
+ // Check that we're open.
+ R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
+ R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
+
+ // Check that we're not terminating.
+ R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(),
+ ResultTerminationRequested);
+
+ // If we have a request available, use it.
+ if (auto head = m_request_list.begin(); head != m_request_list.end()) {
+ // Set our current request.
+ m_current_request = std::addressof(*head);
+ m_current_request->Open();
+
+ // Set our server thread id.
+ m_server_thread_id = GetCurrentThread(m_kernel).GetId();
+
+ // Copy the client request data.
+ std::memcpy(GetCurrentThread(m_kernel).GetLightSessionData(),
+ m_current_request->GetLightSessionData(), KLightSession::DataSize);
+
+ // We successfully received.
+ R_SUCCEED();
+ }
+
+ // We need to wait for a request to come in.
+
+ // Check if we were cancelled.
+ if (GetCurrentThread(m_kernel).IsWaitCancelled()) {
+ GetCurrentThread(m_kernel).ClearWaitCancelled();
+ R_THROW(ResultCancelled);
+ }
+
+ // Mark ourselves as cancellable.
+ GetCurrentThread(m_kernel).SetCancellable();
+
+ // Wait for a request to come in.
+ m_server_thread = GetCurrentThreadPointer(m_kernel);
+ GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
+ GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
+ }
+
+ // We waited to receive a request; if our wait failed, return the failing result.
+ R_TRY(GetCurrentThread(m_kernel).GetWaitResult());
+ }
+}
+
+void KLightServerSession::CleanupRequests() {
+ // Cleanup all pending requests.
+ {
+ KScopedSchedulerLock sl(m_kernel);
+
+ // Handle the current request.
+ if (m_current_request != nullptr) {
+ // Reply to the current request.
+ if (!m_current_request->IsTerminationRequested()) {
+ m_current_request->EndWait(ResultSessionClosed);
+ }
+
+ // Clear our current request.
+ m_current_request->Close();
+ m_current_request = nullptr;
+ m_server_thread_id = InvalidThreadId;
+ }
+
+ // Reply to all other requests.
+ for (auto& thread : m_request_list) {
+ thread.EndWait(ResultSessionClosed);
+ }
+
+ // Wait up our server thread, if we have one.
+ if (m_server_thread != nullptr) {
+ m_server_thread->EndWait(ResultSessionClosed);
+ }
+ }
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_server_session.h b/src/core/hle/kernel/k_light_server_session.h
new file mode 100644
index 000000000..8eca3eab6
--- /dev/null
+++ b/src/core/hle/kernel/k_light_server_session.h
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KLightSession;
+
+class KLightServerSession final : public KAutoObject,
+ public Common::IntrusiveListBaseNode<KLightServerSession> {
+ KERNEL_AUTOOBJECT_TRAITS(KLightServerSession, KAutoObject);
+
+private:
+ KLightSession* m_parent{};
+ KThread::WaiterList m_request_list{};
+ KThread* m_current_request{};
+ u64 m_server_thread_id{std::numeric_limits<u64>::max()};
+ KThread* m_server_thread{};
+
+public:
+ explicit KLightServerSession(KernelCore& kernel);
+ ~KLightServerSession();
+
+ void Initialize(KLightSession* parent) {
+ // Set member variables. */
+ m_parent = parent;
+ }
+
+ virtual void Destroy() override;
+
+ constexpr const KLightSession* GetParent() const {
+ return m_parent;
+ }
+
+ Result OnRequest(KThread* request_thread);
+ Result ReplyAndReceive(u32* data);
+
+ void OnClientClosed();
+
+private:
+ void CleanupRequests();
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_session.cpp b/src/core/hle/kernel/k_light_session.cpp
new file mode 100644
index 000000000..d8b1e6958
--- /dev/null
+++ b/src/core/hle/kernel/k_light_session.cpp
@@ -0,0 +1,81 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/kernel/k_client_port.h"
+#include "core/hle/kernel/k_light_client_session.h"
+#include "core/hle/kernel/k_light_server_session.h"
+#include "core/hle/kernel/k_light_session.h"
+#include "core/hle/kernel/k_process.h"
+
+namespace Kernel {
+
+KLightSession::KLightSession(KernelCore& kernel)
+ : KAutoObjectWithSlabHeapAndContainer(kernel), m_server(kernel), m_client(kernel) {}
+KLightSession::~KLightSession() = default;
+
+void KLightSession::Initialize(KClientPort* client_port, uintptr_t 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
+ // this object will be destroyed.
+ this->Open();
+
+ // Create our sub sessions.
+ KAutoObject::Create(std::addressof(m_server));
+ KAutoObject::Create(std::addressof(m_client));
+
+ // Initialize our sub sessions.
+ m_server.Initialize(this);
+ m_client.Initialize(this);
+
+ // Set state and name.
+ m_state = State::Normal;
+ m_name = name;
+
+ // Set our owner process.
+ m_process = GetCurrentProcessPointer(m_kernel);
+ m_process->Open();
+
+ // Set our port.
+ m_port = client_port;
+ if (m_port != nullptr) {
+ m_port->Open();
+ }
+
+ // Mark initialized.
+ m_initialized = true;
+}
+
+void KLightSession::Finalize() {
+ if (m_port != nullptr) {
+ m_port->OnSessionFinalized();
+ m_port->Close();
+ }
+}
+
+void KLightSession::OnServerClosed() {
+ if (m_state == State::Normal) {
+ m_state = State::ServerClosed;
+ m_client.OnServerClosed();
+ }
+
+ this->Close();
+}
+
+void KLightSession::OnClientClosed() {
+ if (m_state == State::Normal) {
+ m_state = State::ClientClosed;
+ m_server.OnClientClosed();
+ }
+
+ this->Close();
+}
+
+void KLightSession::PostDestroy(uintptr_t arg) {
+ // Release the session count resource the owner process holds.
+ KProcess* owner = reinterpret_cast<KProcess*>(arg);
+ owner->ReleaseResource(Svc::LimitableResource::SessionCountMax, 1);
+ owner->Close();
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_session.h b/src/core/hle/kernel/k_light_session.h
new file mode 100644
index 000000000..f78d8e689
--- /dev/null
+++ b/src/core/hle/kernel/k_light_session.h
@@ -0,0 +1,86 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/kernel/k_light_client_session.h"
+#include "core/hle/kernel/k_light_server_session.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KClientPort;
+class KProcess;
+
+// TODO: SupportDynamicExpansion for SlabHeap
+class KLightSession final
+ : public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
+ KERNEL_AUTOOBJECT_TRAITS(KLightSession, KAutoObject);
+
+private:
+ enum class State : u8 {
+ Invalid = 0,
+ Normal = 1,
+ ClientClosed = 2,
+ ServerClosed = 3,
+ };
+
+public:
+ static constexpr size_t DataSize = sizeof(u32) * 7;
+ static constexpr u32 ReplyFlag = (1U << 31);
+
+private:
+ KLightServerSession m_server;
+ KLightClientSession m_client;
+ State m_state{State::Invalid};
+ KClientPort* m_port{};
+ uintptr_t m_name{};
+ KProcess* m_process{};
+ bool m_initialized{};
+
+public:
+ explicit KLightSession(KernelCore& kernel);
+ ~KLightSession();
+
+ void Initialize(KClientPort* client_port, uintptr_t name);
+ void Finalize() override;
+
+ bool IsInitialized() const override {
+ return m_initialized;
+ }
+ uintptr_t GetPostDestroyArgument() const override {
+ return reinterpret_cast<uintptr_t>(m_process);
+ }
+
+ static void PostDestroy(uintptr_t arg);
+
+ void OnServerClosed();
+ void OnClientClosed();
+
+ bool IsServerClosed() const {
+ return m_state != State::Normal;
+ }
+ bool IsClientClosed() const {
+ return m_state != State::Normal;
+ }
+
+ Result OnRequest(KThread* request_thread) {
+ R_RETURN(m_server.OnRequest(request_thread));
+ }
+
+ KLightClientSession& GetClientSession() {
+ return m_client;
+ }
+ KLightServerSession& GetServerSession() {
+ return m_server;
+ }
+ const KLightClientSession& GetClientSession() const {
+ return m_client;
+ }
+ const KLightServerSession& GetServerSession() const {
+ return m_server;
+ }
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index 1621ca1d3..e5f5d8028 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -58,4 +58,13 @@ Result KPort::EnqueueSession(KServerSession* session) {
R_SUCCEED();
}
+Result KPort::EnqueueSession(KLightServerSession* session) {
+ KScopedSchedulerLock sl{m_kernel};
+
+ R_UNLESS(m_state == State::Normal, ResultPortClosed);
+
+ m_server.EnqueueSession(session);
+ R_SUCCEED();
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h
index 991be27ab..26f5f14ef 100644
--- a/src/core/hle/kernel/k_port.h
+++ b/src/core/hle/kernel/k_port.h
@@ -13,6 +13,7 @@
namespace Kernel {
+class KLightServerSession;
class KServerSession;
class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
@@ -38,6 +39,7 @@ public:
bool IsServerClosed() const;
Result EnqueueSession(KServerSession* session);
+ Result EnqueueSession(KLightServerSession* session);
KClientPort& GetClientPort() {
return m_client;
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index a29d34bc1..bb6632f58 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -27,12 +27,14 @@ bool KServerPort::IsLight() const {
void KServerPort::CleanupSessions() {
// Ensure our preconditions are met.
if (this->IsLight()) {
- UNIMPLEMENTED();
+ ASSERT(m_session_list.empty());
+ } else {
+ ASSERT(m_light_session_list.empty());
}
// Cleanup the session list.
while (true) {
- // Get the last session in the list
+ // Get the last session in the list.
KServerSession* session = nullptr;
{
KScopedSchedulerLock sl{m_kernel};
@@ -49,6 +51,26 @@ void KServerPort::CleanupSessions() {
break;
}
}
+
+ // Cleanup the light session list.
+ while (true) {
+ // Get the last session in the list.
+ KLightServerSession* session = nullptr;
+ {
+ KScopedSchedulerLock sl{m_kernel};
+ if (!m_light_session_list.empty()) {
+ session = std::addressof(m_light_session_list.front());
+ m_light_session_list.pop_front();
+ }
+ }
+
+ // Close the session.
+ if (session != nullptr) {
+ session->Close();
+ } else {
+ break;
+ }
+ }
}
void KServerPort::Destroy() {
@@ -64,8 +86,7 @@ void KServerPort::Destroy() {
bool KServerPort::IsSignaled() const {
if (this->IsLight()) {
- UNIMPLEMENTED();
- return false;
+ return !m_light_session_list.empty();
} else {
return !m_session_list.empty();
}
@@ -83,6 +104,18 @@ void KServerPort::EnqueueSession(KServerSession* session) {
}
}
+void KServerPort::EnqueueSession(KLightServerSession* session) {
+ ASSERT(this->IsLight());
+
+ KScopedSchedulerLock sl{m_kernel};
+
+ // Add the session to our queue.
+ m_light_session_list.push_back(*session);
+ if (m_light_session_list.size() == 1) {
+ this->NotifyAvailable();
+ }
+}
+
KServerSession* KServerPort::AcceptSession() {
ASSERT(!this->IsLight());
@@ -98,4 +131,19 @@ KServerSession* KServerPort::AcceptSession() {
return session;
}
+KLightServerSession* KServerPort::AcceptLightSession() {
+ ASSERT(this->IsLight());
+
+ KScopedSchedulerLock sl{m_kernel};
+
+ // Return the first session in the list.
+ if (m_light_session_list.empty()) {
+ return nullptr;
+ }
+
+ KLightServerSession* session = std::addressof(m_light_session_list.front());
+ m_light_session_list.pop_front();
+ return session;
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index 625280290..72fdb6734 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -9,6 +9,7 @@
#include "common/intrusive_list.h"
+#include "core/hle/kernel/k_light_server_session.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_synchronization_object.h"
@@ -28,8 +29,10 @@ public:
void Initialize(KPort* parent);
void EnqueueSession(KServerSession* session);
+ void EnqueueSession(KLightServerSession* session);
KServerSession* AcceptSession();
+ KLightServerSession* AcceptLightSession();
const KPort* GetParent() const {
return m_parent;
@@ -43,10 +46,12 @@ public:
private:
using SessionList = Common::IntrusiveListBaseTraits<KServerSession>::ListType;
+ using LightSessionList = Common::IntrusiveListBaseTraits<KLightServerSession>::ListType;
void CleanupSessions();
SessionList m_session_list{};
+ LightSessionList m_light_session_list{};
KPort* m_parent{};
};
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 598ec7878..e33a88e24 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -453,6 +453,11 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
size_t client_buffer_size = request->GetSize();
// bool recv_list_broken = false;
+ if (!client_message) {
+ client_message = GetInteger(client_thread->GetTlsAddress());
+ client_buffer_size = MessageBufferSize;
+ }
+
// Receive the message.
Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
if (out_context != nullptr) {
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index f69bab088..3f4dd5989 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/src/core/hle/kernel/k_session.h
@@ -46,6 +46,10 @@ public:
return this->GetState() != State::Normal;
}
+ Result OnRequest(KSessionRequest* request) {
+ R_RETURN(m_server.OnRequest(request));
+ }
+
KClientSession& GetClientSession() {
return m_client;
}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 390db2409..e9925d231 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -385,6 +385,13 @@ public:
m_cancellable = false;
}
+ u32* GetLightSessionData() const {
+ return m_light_ipc_data;
+ }
+ void SetLightSessionData(u32* data) {
+ m_light_ipc_data = data;
+ }
+
bool IsTerminationRequested() const {
return m_termination_requested || GetRawState() == ThreadState::Terminated;
}
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 032c4e093..8cb05ca0b 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1340,6 +1340,7 @@ struct KernelCore::SlabHeapContainer {
KSlabHeap<KProcess> process;
KSlabHeap<KResourceLimit> resource_limit;
KSlabHeap<KSession> session;
+ KSlabHeap<KLightSession> light_session;
KSlabHeap<KSharedMemory> shared_memory;
KSlabHeap<KSharedMemoryInfo> shared_memory_info;
KSlabHeap<KThread> thread;
@@ -1370,6 +1371,8 @@ KSlabHeap<T>& KernelCore::SlabHeap() {
return slab_heap_container->resource_limit;
} else if constexpr (std::is_same_v<T, KSession>) {
return slab_heap_container->session;
+ } else if constexpr (std::is_same_v<T, KLightSession>) {
+ return slab_heap_container->light_session;
} else if constexpr (std::is_same_v<T, KSharedMemory>) {
return slab_heap_container->shared_memory;
} else if constexpr (std::is_same_v<T, KSharedMemoryInfo>) {
@@ -1407,6 +1410,7 @@ template KSlabHeap<KPort>& KernelCore::SlabHeap();
template KSlabHeap<KProcess>& KernelCore::SlabHeap();
template KSlabHeap<KResourceLimit>& KernelCore::SlabHeap();
template KSlabHeap<KSession>& KernelCore::SlabHeap();
+template KSlabHeap<KLightSession>& KernelCore::SlabHeap();
template KSlabHeap<KSharedMemory>& KernelCore::SlabHeap();
template KSlabHeap<KSharedMemoryInfo>& KernelCore::SlabHeap();
template KSlabHeap<KThread>& KernelCore::SlabHeap();
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 6b5e1cb8d..47a3e7bb0 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -7,59 +7,127 @@
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_hardware_timer.h"
#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_server_session.h"
+#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Svc {
-/// Makes a blocking IPC call to a service.
-Result SendSyncRequest(Core::System& system, Handle handle) {
- // Get the client session from its handle.
+namespace {
+
+Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size,
+ Handle session_handle) {
+ // Get the client session.
KScopedAutoObject session =
- GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle);
+ GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle);
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
- LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle);
+ // Get the parent, and persist a reference to it until we're done.
+ KScopedAutoObject parent = session->GetParent();
+ ASSERT(parent.IsNotNull());
- R_RETURN(session->SendSyncRequest());
+ // Send the request.
+ R_RETURN(session->SendSyncRequest(message, buffer_size));
}
-Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer,
- uint64_t message_buffer_size, Handle session_handle) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
-}
+Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
+ size_t buffer_size, KPhysicalAddress message_paddr,
+ KSynchronizationObject** objs, int32_t num_objects, Handle reply_target,
+ int64_t timeout_ns) {
+ // Reply to the target, if one is specified.
+ if (reply_target != InvalidHandle) {
+ KScopedAutoObject session =
+ GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target);
+ R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
-Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
- uint64_t message_buffer, uint64_t message_buffer_size,
- Handle session_handle) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+ // If we fail to reply, we want to set the output index to -1.
+ ON_RESULT_FAILURE {
+ *out_index = -1;
+ };
+
+ // Send the reply.
+ R_TRY(session->SendReply());
+ // R_TRY(session->SendReply(message, buffer_size, message_paddr));
+ }
+
+ // Receive a message.
+ {
+ // Convert the timeout from nanoseconds to ticks.
+ // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
+ s64 timeout;
+ if (timeout_ns > 0) {
+ const s64 offset_tick(timeout_ns);
+ if (offset_tick > 0) {
+ timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
+ if (timeout <= 0) {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = std::numeric_limits<s64>::max();
+ }
+ } else {
+ timeout = timeout_ns;
+ }
+
+ // Wait for a message.
+ while (true) {
+ // Wait for an object.
+ s32 index;
+ Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs,
+ num_objects, timeout);
+ if (ResultTimedOut == result) {
+ R_THROW(result);
+ }
+
+ // Receive the request.
+ if (R_SUCCEEDED(result)) {
+ KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
+ if (session != nullptr) {
+ // result = session->ReceiveRequest(message, buffer_size, message_paddr);
+ result = session->ReceiveRequest();
+ if (ResultNotFound == result) {
+ continue;
+ }
+ }
+ }
+
+ *out_index = index;
+ R_RETURN(result);
+ }
+ }
}
-Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles,
- Handle reply_target, s64 timeout_ns) {
+Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
+ size_t buffer_size, KPhysicalAddress message_paddr,
+ KProcessAddress user_handles, int32_t num_handles, Handle reply_target,
+ int64_t timeout_ns) {
// Ensure number of handles is valid.
- R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
+ R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);
// Get the synchronization context.
- auto& kernel = system.Kernel();
- auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
- auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer();
- auto handles = GetCurrentThread(kernel).GetHandleBuffer();
+ auto& process = GetCurrentProcess(kernel);
+ auto& thread = GetCurrentThread(kernel);
+ auto& handle_table = process.GetHandleTable();
+ KSynchronizationObject** objs = thread.GetSynchronizationObjectBuffer().data();
+ Handle* handles = thread.GetHandleBuffer().data();
// Copy user handles.
if (num_handles > 0) {
- // Get the handles.
- R_UNLESS(GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(),
- sizeof(Handle) * num_handles),
+ // Ensure that we can try to get the handles.
+ R_UNLESS(process.GetPageTable().Contains(user_handles, num_handles * sizeof(Handle)),
ResultInvalidPointer);
+ // Get the handles
+ R_UNLESS(
+ GetCurrentMemory(kernel).ReadBlock(user_handles, handles, sizeof(Handle) * num_handles),
+ ResultInvalidPointer);
+
// Convert the handles to objects.
- R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(
- objs.data(), handles.data(), num_handles),
- ResultInvalidHandle);
+ R_UNLESS(
+ handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles),
+ ResultInvalidHandle);
}
// Ensure handles are closed when we're done.
@@ -69,69 +137,135 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
}
});
- // Reply to the target, if one is specified.
- if (reply_target != InvalidHandle) {
- KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
- R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+ R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
+ num_handles, reply_target, timeout_ns));
+}
- // If we fail to reply, we want to set the output index to -1.
+} // namespace
+
+/// Makes a blocking IPC call to a service.
+Result SendSyncRequest(Core::System& system, Handle session_handle) {
+ R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle));
+}
+
+Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message, uint64_t buffer_size,
+ Handle session_handle) {
+ auto& kernel = system.Kernel();
+
+ // Validate that the message buffer is page aligned and does not overflow.
+ R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
+ R_UNLESS(buffer_size > 0, ResultInvalidSize);
+ R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
+ R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
+
+ // Get the process page table.
+ auto& page_table = GetCurrentProcess(kernel).GetPageTable();
+
+ // Lock the message buffer.
+ R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size));
+
+ {
+ // If we fail to send the message, unlock the message buffer.
ON_RESULT_FAILURE {
- *out_index = -1;
+ page_table.UnlockForIpcUserBuffer(message, buffer_size);
};
- // Send the reply.
- R_TRY(session->SendReply());
+ // Send the request.
+ ASSERT(message != 0);
+ R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle));
}
- // Convert the timeout from nanoseconds to ticks.
- // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
- s64 timeout;
- if (timeout_ns > 0) {
- const s64 offset_tick(timeout_ns);
- if (offset_tick > 0) {
- timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
- if (timeout <= 0) {
- timeout = std::numeric_limits<s64>::max();
- }
- } else {
- timeout = std::numeric_limits<s64>::max();
- }
- } else {
- timeout = timeout_ns;
- }
+ // We successfully processed, so try to unlock the message buffer.
+ R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
+}
- // Wait for a message.
- while (true) {
- // Wait for an object.
- s32 index;
- Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(),
- num_handles, timeout);
- if (result == ResultTimedOut) {
- R_RETURN(result);
- }
+Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
+ uint64_t message, uint64_t buffer_size,
+ Handle session_handle) {
+ // Get the process and handle table.
+ auto& process = GetCurrentProcess(system.Kernel());
+ auto& handle_table = process.GetHandleTable();
- // Receive the request.
- if (R_SUCCEEDED(result)) {
- KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
- if (session != nullptr) {
- result = session->ReceiveRequest();
- if (result == ResultNotFound) {
- continue;
- }
- }
- }
+ // Reserve a new event from the process resource limit.
+ KScopedResourceReservation event_reservation(std::addressof(process),
+ Svc::LimitableResource::EventCountMax);
+ R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
- *out_index = index;
- R_RETURN(result);
- }
+ // Get the client session.
+ KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(session_handle);
+ R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+
+ // Get the parent, and persist a reference to it until we're done.
+ KScopedAutoObject parent = session->GetParent();
+ ASSERT(parent.IsNotNull());
+
+ // Create a new event.
+ KEvent* event = KEvent::Create(system.Kernel());
+ R_UNLESS(event != nullptr, ResultOutOfResource);
+
+ // Initialize the event.
+ event->Initialize(std::addressof(process));
+
+ // Commit our reservation.
+ event_reservation.Commit();
+
+ // At end of scope, kill the standing references to the sub events.
+ SCOPE_EXIT({
+ event->GetReadableEvent().Close();
+ event->Close();
+ });
+
+ // Register the event.
+ KEvent::Register(system.Kernel(), event);
+
+ // Add the readable event to the handle table.
+ R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent())));
+
+ // Ensure that if we fail to send the request, we close the readable handle.
+ ON_RESULT_FAILURE {
+ handle_table.Remove(*out_event_handle);
+ };
+
+ // Send the async request.
+ R_RETURN(session->SendAsyncRequest(event, message, buffer_size));
}
-Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index,
- uint64_t message_buffer, uint64_t message_buffer_size,
- uint64_t handles, int32_t num_handles, Handle reply_target,
- int64_t timeout_ns) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles,
+ Handle reply_target, s64 timeout_ns) {
+ R_RETURN(ReplyAndReceiveImpl(system.Kernel(), out_index, 0, 0, 0, handles, num_handles,
+ reply_target, timeout_ns));
+}
+
+Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message,
+ uint64_t buffer_size, uint64_t handles, int32_t num_handles,
+ Handle reply_target, int64_t timeout_ns) {
+ // Validate that the message buffer is page aligned and does not overflow.
+ R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
+ R_UNLESS(buffer_size > 0, ResultInvalidSize);
+ R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
+ R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
+
+ // Get the process page table.
+ auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
+
+ // Lock the message buffer, getting its physical address.
+ KPhysicalAddress message_paddr;
+ R_TRY(page_table.LockForIpcUserBuffer(std::addressof(message_paddr), message, buffer_size));
+
+ {
+ // If we fail to send the message, unlock the message buffer.
+ ON_RESULT_FAILURE {
+ page_table.UnlockForIpcUserBuffer(message, buffer_size);
+ };
+
+ // Reply/Receive the request.
+ ASSERT(message != 0);
+ R_TRY(ReplyAndReceiveImpl(system.Kernel(), out_index, message, buffer_size, message_paddr,
+ handles, num_handles, reply_target, timeout_ns));
+ }
+
+ // We successfully processed, so try to unlock the message buffer.
+ R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
}
Result SendSyncRequest64(Core::System& system, Handle session_handle) {
diff --git a/src/core/hle/kernel/svc/svc_light_ipc.cpp b/src/core/hle/kernel/svc/svc_light_ipc.cpp
index d757d5af2..4772cbda1 100644
--- a/src/core/hle/kernel/svc/svc_light_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp
@@ -1,21 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "core/arm/arm_interface.h"
#include "core/core.h"
+#include "core/hle/kernel/k_light_client_session.h"
+#include "core/hle/kernel/k_light_server_session.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel::Svc {
Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+ // Get the light client session from its handle.
+ KScopedAutoObject session = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KLightClientSession>(session_handle);
+ R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+
+ // Send the request.
+ R_TRY(session->SendSyncRequest(args));
+
+ R_SUCCEED();
}
Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+ // Get the light server session from its handle.
+ KScopedAutoObject session = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KLightServerSession>(session_handle);
+ R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
+
+ // Handle the request.
+ R_TRY(session->ReplyAndReceive(args));
+
+ R_SUCCEED();
}
Result SendSyncRequestLight64(Core::System& system, Handle session_handle, u32* args) {
diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp
index abba757c7..737749f7d 100644
--- a/src/core/hle/kernel/svc/svc_port.cpp
+++ b/src/core/hle/kernel/svc/svc_port.cpp
@@ -5,6 +5,7 @@
#include "core/core.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_light_client_session.h"
#include "core/hle/kernel/k_object_name.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
@@ -51,13 +52,73 @@ Result ConnectToNamedPort(Core::System& system, Handle* out, u64 user_name) {
Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
int32_t max_sessions, bool is_light, uint64_t name) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+ auto& kernel = system.Kernel();
+
+ // Ensure max sessions is valid.
+ R_UNLESS(max_sessions > 0, ResultOutOfRange);
+
+ // Get the current handle table.
+ auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
+
+ // Create a new port.
+ KPort* port = KPort::Create(kernel);
+ R_UNLESS(port != nullptr, ResultOutOfResource);
+
+ // Initialize the port.
+ port->Initialize(max_sessions, is_light, name);
+
+ // Ensure that we clean up the port (and its only references are handle table) on function end.
+ SCOPE_EXIT({
+ port->GetServerPort().Close();
+ port->GetClientPort().Close();
+ });
+
+ // Register the port.
+ KPort::Register(kernel, port);
+
+ // Add the client to the handle table.
+ R_TRY(handle_table.Add(out_client, std::addressof(port->GetClientPort())));
+
+ // Ensure that we maintain a clean handle state on exit.
+ ON_RESULT_FAILURE {
+ handle_table.Remove(*out_client);
+ };
+
+ // Add the server to the handle table.
+ R_RETURN(handle_table.Add(out_server, std::addressof(port->GetServerPort())));
}
-Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+Result ConnectToPort(Core::System& system, Handle* out, Handle port) {
+ // Get the current handle table.
+ auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+
+ // Get the client port.
+ KScopedAutoObject client_port = handle_table.GetObject<KClientPort>(port);
+ R_UNLESS(client_port.IsNotNull(), ResultInvalidHandle);
+
+ // Reserve a handle for the port.
+ // NOTE: Nintendo really does write directly to the output handle here.
+ R_TRY(handle_table.Reserve(out));
+ ON_RESULT_FAILURE {
+ handle_table.Unreserve(*out);
+ };
+
+ // Create the session.
+ KAutoObject* session;
+ if (client_port->IsLight()) {
+ R_TRY(client_port->CreateLightSession(
+ reinterpret_cast<KLightClientSession**>(std::addressof(session))));
+ } else {
+ R_TRY(client_port->CreateSession(
+ reinterpret_cast<KClientSession**>(std::addressof(session))));
+ }
+
+ // Register the session.
+ handle_table.Register(*out, session);
+ session->Close();
+
+ // We succeeded.
+ R_SUCCEED();
}
Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name,
diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp
index 01b8a52ad..2f5905f32 100644
--- a/src/core/hle/kernel/svc/svc_session.cpp
+++ b/src/core/hle/kernel/svc/svc_session.cpp
@@ -3,8 +3,10 @@
#include "common/scope_exit.h"
#include "core/core.h"
+#include "core/hle/kernel/k_light_session.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scoped_resource_reservation.h"
+#include "core/hle/kernel/k_server_port.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/svc.h"
@@ -20,7 +22,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
T* session;
// Reserve a new session from the process resource limit.
- // FIXME: LimitableResource_SessionCountMax
+ // TODO: Dynamic resource limits
KScopedResourceReservation session_reservation(std::addressof(process),
LimitableResource::SessionCountMax);
if (session_reservation.Succeeded()) {
@@ -92,16 +94,42 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, bool is_light,
u64 name) {
if (is_light) {
- // return CreateSession<KLightSession>(system, out_server, out_client, name);
- R_THROW(ResultNotImplemented);
+ R_RETURN(CreateSession<KLightSession>(system, out_server, out_client, name));
} else {
R_RETURN(CreateSession<KSession>(system, out_server, out_client, name));
}
}
-Result AcceptSession(Core::System& system, Handle* out_handle, Handle port_handle) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+Result AcceptSession(Core::System& system, Handle* out, Handle port_handle) {
+ // Get the current handle table.
+ auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
+
+ // Get the server port.
+ KScopedAutoObject port = handle_table.GetObject<KServerPort>(port_handle);
+ R_UNLESS(port.IsNotNull(), ResultInvalidHandle);
+
+ // Reserve an entry for the new session.
+ R_TRY(handle_table.Reserve(out));
+ ON_RESULT_FAILURE {
+ handle_table.Unreserve(*out);
+ };
+
+ // Accept the session.
+ KAutoObject* session;
+ if (port->IsLight()) {
+ session = port->AcceptLightSession();
+ } else {
+ session = port->AcceptSession();
+ }
+
+ // Ensure we accepted successfully.
+ R_UNLESS(session != nullptr, ResultNotFound);
+
+ // Register the session.
+ handle_table.Register(*out, session);
+ session->Close();
+
+ R_SUCCEED();
}
Result CreateSession64(Core::System& system, Handle* out_server_session_handle,
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index d7db24f42..75bf31e32 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -171,6 +171,7 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han
buffer->height = SharedBufferHeight;
buffer->stride = SharedBufferBlockLinearStride;
buffer->format = SharedBufferBlockLinearFormat;
+ buffer->external_format = SharedBufferBlockLinearFormat;
buffer->buffer_id = handle;
buffer->offset = slot * SharedBufferSlotSize;
ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 53209537f..f2cdefa1c 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -192,8 +192,6 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
return result;
}
- LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
-
*out_client_session = session;
return ResultSuccess;
}