Skip to content
Snippets Groups Projects
Unverified Commit 875568bb authored by liamwhite's avatar liamwhite Committed by GitHub
Browse files

Merge pull request #12296 from liamwhite/client-session

kernel: implement remaining IPC syscalls
parents 988e557e 40bb176c
No related branches found
No related tags found
No related merge requests found
...@@ -10,9 +10,7 @@ ...@@ -10,9 +10,7 @@
namespace Kernel { namespace Kernel {
static constexpr u32 MessageBufferSize = 0x100; KClientSession::KClientSession(KernelCore& kernel) : KAutoObject{kernel} {}
KClientSession::KClientSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
KClientSession::~KClientSession() = default; KClientSession::~KClientSession() = default;
void KClientSession::Destroy() { void KClientSession::Destroy() {
...@@ -22,18 +20,30 @@ void KClientSession::Destroy() { ...@@ -22,18 +20,30 @@ void KClientSession::Destroy() {
void KClientSession::OnServerClosed() {} 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. // Create a session request.
KSessionRequest* request = KSessionRequest::Create(m_kernel); KSessionRequest* request = KSessionRequest::Create(m_kernel);
R_UNLESS(request != nullptr, ResultOutOfResource); R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); }); SCOPE_EXIT({ request->Close(); });
// Initialize the request. // Initialize the request.
request->Initialize(nullptr, GetInteger(GetCurrentThread(m_kernel).GetTlsAddress()), request->Initialize(event, address, size);
MessageBufferSize);
// Send the request. // Send the request.
R_RETURN(m_parent->GetServerSession().OnRequest(request)); R_RETURN(m_parent->OnRequest(request));
} }
} // namespace Kernel } // namespace Kernel
...@@ -9,24 +9,12 @@ ...@@ -9,24 +9,12 @@
#include "core/hle/kernel/slab_helpers.h" #include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h" #include "core/hle/result.h"
union Result;
namespace Core::Memory {
class Memory;
}
namespace Core::Timing {
class CoreTiming;
}
namespace Kernel { namespace Kernel {
class KernelCore; class KernelCore;
class KSession; class KSession;
class KThread;
class KClientSession final class KClientSession final : public KAutoObject {
: public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject); KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
public: public:
...@@ -39,13 +27,13 @@ public: ...@@ -39,13 +27,13 @@ public:
} }
void Destroy() override; void Destroy() override;
static void PostDestroy(uintptr_t arg) {}
KSession* GetParent() const { KSession* GetParent() const {
return m_parent; 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(); void OnServerClosed();
......
...@@ -453,6 +453,11 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext ...@@ -453,6 +453,11 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
size_t client_buffer_size = request->GetSize(); size_t client_buffer_size = request->GetSize();
// bool recv_list_broken = false; // bool recv_list_broken = false;
if (!client_message) {
client_message = GetInteger(client_thread->GetTlsAddress());
client_buffer_size = MessageBufferSize;
}
// Receive the message. // Receive the message.
Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
if (out_context != nullptr) { if (out_context != nullptr) {
......
...@@ -46,6 +46,10 @@ public: ...@@ -46,6 +46,10 @@ public:
return this->GetState() != State::Normal; return this->GetState() != State::Normal;
} }
Result OnRequest(KSessionRequest* request) {
R_RETURN(m_server.OnRequest(request));
}
KClientSession& GetClientSession() { KClientSession& GetClientSession() {
return m_client; return m_client;
} }
......
...@@ -7,59 +7,127 @@ ...@@ -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. namespace {
Result SendSyncRequest(Core::System& system, Handle handle) {
// Get the client session from its handle. 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, Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
uint64_t message_buffer_size, Handle session_handle) { size_t buffer_size, KPhysicalAddress message_paddr,
UNIMPLEMENTED(); KSynchronizationObject** objs, int32_t num_objects, Handle reply_target,
R_THROW(ResultNotImplemented); 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, // If we fail to reply, we want to set the output index to -1.
uint64_t message_buffer, uint64_t message_buffer_size, ON_RESULT_FAILURE {
Handle session_handle) { *out_index = -1;
UNIMPLEMENTED(); };
R_THROW(ResultNotImplemented);
// 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, Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
Handle reply_target, s64 timeout_ns) { 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& process = GetCurrentProcess(kernel);
auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); auto& thread = GetCurrentThread(kernel);
auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); auto& handle_table = process.GetHandleTable();
auto handles = GetCurrentThread(kernel).GetHandleBuffer(); 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. // Ensure that we can try to get the handles.
R_UNLESS(GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), R_UNLESS(process.GetPageTable().Contains(user_handles, num_handles * sizeof(Handle)),
sizeof(Handle) * num_handles),
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>( R_UNLESS(
objs.data(), handles.data(), num_handles), handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles),
ResultInvalidHandle); 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 ...@@ -69,69 +137,135 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
} }
}); });
// Reply to the target, if one is specified. R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
if (reply_target != InvalidHandle) { num_handles, reply_target, timeout_ns));
KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target); }
R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
// 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. // Send the request.
R_TRY(session->SendReply()); ASSERT(message != 0);
R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle));
} }
// Convert the timeout from nanoseconds to ticks. // We successfully processed, so try to unlock the message buffer.
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization... R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
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. Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
while (true) { uint64_t message, uint64_t buffer_size,
// Wait for an object. Handle session_handle) {
s32 index; // Get the process and handle table.
Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), auto& process = GetCurrentProcess(system.Kernel());
num_handles, timeout); auto& handle_table = process.GetHandleTable();
if (result == ResultTimedOut) {
R_RETURN(result);
}
// Receive the request. // Reserve a new event from the process resource limit.
if (R_SUCCEEDED(result)) { KScopedResourceReservation event_reservation(std::addressof(process),
KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); Svc::LimitableResource::EventCountMax);
if (session != nullptr) { R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
result = session->ReceiveRequest();
if (result == ResultNotFound) {
continue;
}
}
}
*out_index = index; // Get the client session.
R_RETURN(result); 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, Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles,
uint64_t message_buffer, uint64_t message_buffer_size, Handle reply_target, s64 timeout_ns) {
uint64_t handles, int32_t num_handles, Handle reply_target, R_RETURN(ReplyAndReceiveImpl(system.Kernel(), out_index, 0, 0, 0, handles, num_handles,
int64_t timeout_ns) { reply_target, timeout_ns));
UNIMPLEMENTED(); }
R_THROW(ResultNotImplemented);
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) {
......
...@@ -192,8 +192,6 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques ...@@ -192,8 +192,6 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
return result; return result;
} }
LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
*out_client_session = session; *out_client_session = session;
return ResultSuccess; return ResultSuccess;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment