|
|
|
@ -7,71 +7,39 @@ |
|
|
|
#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.
|
|
|
|
KScopedAutoObject session = |
|
|
|
GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle); |
|
|
|
R_UNLESS(session.IsNotNull(), ResultInvalidHandle); |
|
|
|
|
|
|
|
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle); |
|
|
|
|
|
|
|
R_RETURN(session->SendSyncRequest()); |
|
|
|
} |
|
|
|
|
|
|
|
Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer, |
|
|
|
uint64_t message_buffer_size, Handle session_handle) { |
|
|
|
UNIMPLEMENTED(); |
|
|
|
R_THROW(ResultNotImplemented); |
|
|
|
} |
|
|
|
namespace { |
|
|
|
|
|
|
|
Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle, |
|
|
|
uint64_t message_buffer, uint64_t message_buffer_size, |
|
|
|
Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size, |
|
|
|
Handle session_handle) { |
|
|
|
UNIMPLEMENTED(); |
|
|
|
R_THROW(ResultNotImplemented); |
|
|
|
} |
|
|
|
|
|
|
|
Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, |
|
|
|
Handle reply_target, s64 timeout_ns) { |
|
|
|
// Ensure number of handles is valid.
|
|
|
|
R_UNLESS(0 <= num_handles && num_handles <= 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(); |
|
|
|
|
|
|
|
// Copy user handles.
|
|
|
|
if (num_handles > 0) { |
|
|
|
// Get the handles.
|
|
|
|
R_UNLESS(GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), |
|
|
|
sizeof(Handle) * num_handles), |
|
|
|
ResultInvalidPointer); |
|
|
|
// Get the client session.
|
|
|
|
KScopedAutoObject session = |
|
|
|
GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle); |
|
|
|
R_UNLESS(session.IsNotNull(), ResultInvalidHandle); |
|
|
|
|
|
|
|
// Convert the handles to objects.
|
|
|
|
R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>( |
|
|
|
objs.data(), handles.data(), num_handles), |
|
|
|
ResultInvalidHandle); |
|
|
|
} |
|
|
|
// Get the parent, and persist a reference to it until we're done.
|
|
|
|
KScopedAutoObject parent = session->GetParent(); |
|
|
|
ASSERT(parent.IsNotNull()); |
|
|
|
|
|
|
|
// Ensure handles are closed when we're done.
|
|
|
|
SCOPE_EXIT({ |
|
|
|
for (auto i = 0; i < num_handles; ++i) { |
|
|
|
objs[i]->Close(); |
|
|
|
// Send the request.
|
|
|
|
R_RETURN(session->SendSyncRequest(message, buffer_size)); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
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 = handle_table.GetObject<KServerSession>(reply_target); |
|
|
|
KScopedAutoObject session = |
|
|
|
GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target); |
|
|
|
R_UNLESS(session.IsNotNull(), ResultInvalidHandle); |
|
|
|
|
|
|
|
// If we fail to reply, we want to set the output index to -1.
|
|
|
|
@ -81,8 +49,11 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad |
|
|
|
|
|
|
|
// 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; |
|
|
|
@ -104,18 +75,19 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad |
|
|
|
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 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 (result == ResultNotFound) { |
|
|
|
if (ResultNotFound == result) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -125,13 +97,175 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad |
|
|
|
R_RETURN(result); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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, |
|
|
|
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) { |
|
|
|
UNIMPLEMENTED(); |
|
|
|
R_THROW(ResultNotImplemented); |
|
|
|
// Ensure number of handles is valid.
|
|
|
|
R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange); |
|
|
|
|
|
|
|
// Get the synchronization context.
|
|
|
|
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) { |
|
|
|
// 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, handles, num_handles), |
|
|
|
ResultInvalidHandle); |
|
|
|
} |
|
|
|
|
|
|
|
// Ensure handles are closed when we're done.
|
|
|
|
SCOPE_EXIT({ |
|
|
|
for (auto i = 0; i < num_handles; ++i) { |
|
|
|
objs[i]->Close(); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs, |
|
|
|
num_handles, reply_target, timeout_ns)); |
|
|
|
} |
|
|
|
|
|
|
|
} // 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 { |
|
|
|
page_table.UnlockForIpcUserBuffer(message, buffer_size); |
|
|
|
}; |
|
|
|
|
|
|
|
// Send the request.
|
|
|
|
ASSERT(message != 0); |
|
|
|
R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle)); |
|
|
|
} |
|
|
|
|
|
|
|
// We successfully processed, so try to unlock the message buffer.
|
|
|
|
R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size)); |
|
|
|
} |
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
// Reserve a new event from the process resource limit.
|
|
|
|
KScopedResourceReservation event_reservation(std::addressof(process), |
|
|
|
Svc::LimitableResource::EventCountMax); |
|
|
|
R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); |
|
|
|
|
|
|
|
// 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 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) { |
|
|
|
|