aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/svc
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/svc')
-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
4 files changed, 339 insertions, 97 deletions
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,