|
@@ -7,59 +7,127 @@
|
|
|
#include "core/hle/kernel/k_client_session.h"
|
|
#include "core/hle/kernel/k_client_session.h"
|
|
|
#include "core/hle/kernel/k_hardware_timer.h"
|
|
#include "core/hle/kernel/k_hardware_timer.h"
|
|
|
#include "core/hle/kernel/k_process.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_server_session.h"
|
|
|
|
|
+#include "core/hle/kernel/k_session.h"
|
|
|
#include "core/hle/kernel/svc.h"
|
|
#include "core/hle/kernel/svc.h"
|
|
|
#include "core/hle/kernel/svc_results.h"
|
|
#include "core/hle/kernel/svc_results.h"
|
|
|
|
|
|
|
|
namespace Kernel::Svc {
|
|
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 =
|
|
KScopedAutoObject session =
|
|
|
- GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle);
|
|
|
|
|
|
|
+ GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle);
|
|
|
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
|
|
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.
|
|
// 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.
|
|
// 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.
|
|
// Copy user handles.
|
|
|
if (num_handles > 0) {
|
|
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);
|
|
ResultInvalidPointer);
|
|
|
|
|
|
|
|
|
|
+ // Get the handles
|
|
|
|
|
+ R_UNLESS(
|
|
|
|
|
+ GetCurrentMemory(kernel).ReadBlock(user_handles, handles, sizeof(Handle) * num_handles),
|
|
|
|
|
+ ResultInvalidPointer);
|
|
|
|
|
+
|
|
|
// Convert the handles to objects.
|
|
// 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.
|
|
// 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 {
|
|
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) {
|
|
Result SendSyncRequest64(Core::System& system, Handle session_handle) {
|