diff options
| author | liamwhite <liamwhite@users.noreply.github.com> | 2023-12-09 19:03:56 -0500 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-09 19:03:56 -0500 | 
| commit | 875568bb3e34725578f7fa3661c8bad89f23a173 (patch) | |
| tree | 966c08ba52f4e786765eef72848013a4c7aa559f | |
| parent | 988e557ec81a9f4b883e9089fedd6079f76e07e9 (diff) | |
| parent | 40bb176c396b7af6ca50d2e26561cbb20d2e40d1 (diff) | |
Merge pull request #12296 from liamwhite/client-session
kernel: implement remaining IPC syscalls
| -rw-r--r-- | src/core/hle/kernel/k_client_session.cpp | 24 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_client_session.h | 18 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_server_session.cpp | 5 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_session.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc/svc_ipc.cpp | 296 | ||||
| -rw-r--r-- | src/core/hle/service/sm/sm.cpp | 2 | 
6 files changed, 244 insertions, 105 deletions
| 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_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index 3ea653163..ec6812d5a 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/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/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 9ab718e0a..e0cde9a05 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;  } | 
