From 8922bbd10877a72bf9cd474a90bd2a1e06b850ce Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 29 Jul 2020 03:57:40 -0700 Subject: [PATCH] kern: SendSyncRequestLight, ReplyAndReceiveLight --- .../mesosphere/kern_k_light_session.hpp | 3 + .../include/mesosphere/kern_k_thread.hpp | 3 + .../arch/arm64/svc/kern_svc_light_ipc_asm.s | 156 ++++++++++++++++++ .../source/arch/arm64/svc/kern_svc_tables.cpp | 13 ++ .../source/kern_k_light_client_session.cpp | 19 ++- .../source/kern_k_light_server_session.cpp | 140 +++++++++++++++- .../libmesosphere/source/kern_k_thread.cpp | 4 +- .../source/svc/kern_svc_light_ipc.cpp | 36 +++- 8 files changed, 360 insertions(+), 14 deletions(-) create mode 100644 libraries/libmesosphere/source/arch/arm64/svc/kern_svc_light_ipc_asm.s diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp index 2e3ad41c1..d37f69b48 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_session.hpp @@ -34,6 +34,9 @@ namespace ams::kern { ClientClosed = 2, ServerClosed = 3, }; + public: + static constexpr size_t DataSize = sizeof(u32) * 7; + static constexpr u32 ReplyFlag = (1u << (BITSIZEOF(u32) - 1)); private: KLightServerSession server; KLightClientSession client; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 43717e950..c125da29e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -410,6 +410,9 @@ namespace ams::kern { void ClearCancellable() { this->cancellable = false; } void SetCancellable() { this->cancellable = true; } + constexpr u32 *GetLightSessionData() const { return this->light_ipc_data; } + constexpr void SetLightSessionData(u32 *data) { this->light_ipc_data = data; } + bool HasWaiters() const { return !this->waiter_list.empty(); } constexpr s64 GetLastScheduledTick() const { return this->last_scheduled_tick; } diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_light_ipc_asm.s b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_light_ipc_asm.s new file mode 100644 index 000000000..acc64aa19 --- /dev/null +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_light_ipc_asm.s @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* ams::kern::svc::CallSendSyncRequestLight64() */ +.section .text._ZN3ams4kern3svc26CallSendSyncRequestLight64Ev, "ax", %progbits +.global _ZN3ams4kern3svc26CallSendSyncRequestLight64Ev +.type _ZN3ams4kern3svc26CallSendSyncRequestLight64Ev, %function +_ZN3ams4kern3svc26CallSendSyncRequestLight64Ev: + /* Allocate space for the light ipc data. */ + sub sp, sp, #(4 * 8) + + /* Store the light ipc data. */ + stp w1, w2, [sp, #(4 * 0)] + stp w3, w4, [sp, #(4 * 2)] + stp w5, w6, [sp, #(4 * 4)] + str w7, [sp, #(4 * 6)] + + /* Invoke the svc handler. */ + mov x1, sp + stp x29, x30, [sp, #-16]! + bl _ZN3ams4kern3svc22SendSyncRequestLight64EjPj + ldp x29, x30, [sp], #16 + + /* Load the light ipc data. */ + ldp w1, w2, [sp, #(4 * 0)] + ldp w3, w4, [sp, #(4 * 2)] + ldp w5, w6, [sp, #(4 * 4)] + ldr w7, [sp, #(4 * 6)] + + /* Free the stack space for the light ipc data. */ + add sp, sp, #(4 * 8) + + ret + +/* ams::kern::svc::CallSendSyncRequestLight64From32() */ +.section .text._ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev, "ax", %progbits +.global _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev +.type _ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev, %function +_ZN3ams4kern3svc32CallSendSyncRequestLight64From32Ev: + /* Load x4-x7 from where the svc handler stores them. */ + ldp x4, x5, [sp, #(8 * 0)] + ldp x6, x7, [sp, #(8 * 2)] + + /* Allocate space for the light ipc data. */ + sub sp, sp, #(4 * 8) + + /* Store the light ipc data. */ + stp w1, w2, [sp, #(4 * 0)] + stp w3, w4, [sp, #(4 * 2)] + stp w5, w6, [sp, #(4 * 4)] + str w7, [sp, #(4 * 6)] + + /* Invoke the svc handler. */ + mov x1, sp + stp x29, x30, [sp, #-16]! + bl _ZN3ams4kern3svc28SendSyncRequestLight64From32EjPj + ldp x29, x30, [sp], #16 + + /* Load the light ipc data. */ + ldp w1, w2, [sp, #(4 * 0)] + ldp w3, w4, [sp, #(4 * 2)] + ldp w5, w6, [sp, #(4 * 4)] + ldr w7, [sp, #(4 * 6)] + + /* Free the stack space for the light ipc data. */ + add sp, sp, #(4 * 8) + + /* Save x4-x7 to where the svc handler stores them. */ + stp x4, x5, [sp, #(8 * 0)] + stp x6, x7, [sp, #(8 * 2)] + + ret + + +/* ams::kern::svc::CallReplyAndReceiveLight64() */ +.section .text._ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev, "ax", %progbits +.global _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev +.type _ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev, %function +_ZN3ams4kern3svc26CallReplyAndReceiveLight64Ev: + /* Allocate space for the light ipc data. */ + sub sp, sp, #(4 * 8) + + /* Store the light ipc data. */ + stp w1, w2, [sp, #(4 * 0)] + stp w3, w4, [sp, #(4 * 2)] + stp w5, w6, [sp, #(4 * 4)] + str w7, [sp, #(4 * 6)] + + /* Invoke the svc handler. */ + mov x1, sp + stp x29, x30, [sp, #-16]! + bl _ZN3ams4kern3svc22ReplyAndReceiveLight64EjPj + ldp x29, x30, [sp], #16 + + /* Load the light ipc data. */ + ldp w1, w2, [sp, #(4 * 0)] + ldp w3, w4, [sp, #(4 * 2)] + ldp w5, w6, [sp, #(4 * 4)] + ldr w7, [sp, #(4 * 6)] + + /* Free the stack space for the light ipc data. */ + add sp, sp, #(4 * 8) + + ret + +/* ams::kern::svc::CallReplyAndReceiveLight64From32() */ +.section .text._ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev, "ax", %progbits +.global _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev +.type _ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev, %function +_ZN3ams4kern3svc32CallReplyAndReceiveLight64From32Ev: + /* Load x4-x7 from where the svc handler stores them. */ + ldp x4, x5, [sp, #(8 * 0)] + ldp x6, x7, [sp, #(8 * 2)] + + /* Allocate space for the light ipc data. */ + sub sp, sp, #(4 * 8) + + /* Store the light ipc data. */ + stp w1, w2, [sp, #(4 * 0)] + stp w3, w4, [sp, #(4 * 2)] + stp w5, w6, [sp, #(4 * 4)] + str w7, [sp, #(4 * 6)] + + /* Invoke the svc handler. */ + mov x1, sp + stp x29, x30, [sp, #-16]! + bl _ZN3ams4kern3svc28ReplyAndReceiveLight64From32EjPj + ldp x29, x30, [sp], #16 + + /* Load the light ipc data. */ + ldp w1, w2, [sp, #(4 * 0)] + ldp w3, w4, [sp, #(4 * 2)] + ldp w5, w6, [sp, #(4 * 4)] + ldr w7, [sp, #(4 * 6)] + + /* Free the stack space for the light ipc data. */ + add sp, sp, #(4 * 8) + + /* Save x4-x7 to where the svc handler stores them. */ + stp x4, x5, [sp, #(8 * 0)] + stp x6, x7, [sp, #(8 * 2)] + + ret diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp index 9e7510f1f..3bb4b84b4 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp @@ -22,6 +22,13 @@ namespace ams::kern::svc { + /* Declare special prototypes for the light ipc handlers. */ + void CallSendSyncRequestLight64(); + void CallSendSyncRequestLight64From32(); + + void CallReplyAndReceiveLight64(); + void CallReplyAndReceiveLight64From32(); + namespace { #ifndef MESOSPHERE_USE_STUBBED_SVC_TABLES @@ -59,6 +66,9 @@ namespace ams::kern::svc { AMS_SVC_FOREACH_KERN_DEFINITION(AMS_KERN_SVC_SET_TABLE_ENTRY, _) #undef AMS_KERN_SVC_SET_TABLE_ENTRY + table[svc::SvcId_SendSyncRequestLight] = CallSendSyncRequestLight64From32; + table[svc::SvcId_ReplyAndReceiveLight] = CallReplyAndReceiveLight64From32; + return table; }(); @@ -70,6 +80,9 @@ namespace ams::kern::svc { AMS_SVC_FOREACH_KERN_DEFINITION(AMS_KERN_SVC_SET_TABLE_ENTRY, _) #undef AMS_KERN_SVC_SET_TABLE_ENTRY + table[svc::SvcId_SendSyncRequestLight] = CallSendSyncRequestLight64; + table[svc::SvcId_ReplyAndReceiveLight] = CallReplyAndReceiveLight64; + return table; }(); diff --git a/libraries/libmesosphere/source/kern_k_light_client_session.cpp b/libraries/libmesosphere/source/kern_k_light_client_session.cpp index b2c5686ef..deacc8ee0 100644 --- a/libraries/libmesosphere/source/kern_k_light_client_session.cpp +++ b/libraries/libmesosphere/source/kern_k_light_client_session.cpp @@ -30,7 +30,24 @@ namespace ams::kern { Result KLightClientSession::SendSyncRequest(u32 *data) { MESOSPHERE_ASSERT_THIS(); - MESOSPHERE_UNIMPLEMENTED(); + /* Get the request thread. */ + KThread *cur_thread = GetCurrentThreadPointer(); + + /* Set the light data. */ + cur_thread->SetLightSessionData(data); + + /* Send the request. */ + { + KScopedSchedulerLock sl; + + cur_thread->SetSyncedObject(nullptr, ResultSuccess()); + + R_TRY(this->parent->OnRequest(cur_thread)); + } + + /* Get the result. */ + KSynchronizationObject *dummy; + return cur_thread->GetWaitResult(std::addressof(dummy)); } } diff --git a/libraries/libmesosphere/source/kern_k_light_server_session.cpp b/libraries/libmesosphere/source/kern_k_light_server_session.cpp index c3995191c..7c3cc1ce9 100644 --- a/libraries/libmesosphere/source/kern_k_light_server_session.cpp +++ b/libraries/libmesosphere/source/kern_k_light_server_session.cpp @@ -27,18 +27,152 @@ namespace ams::kern { void KLightServerSession::OnClientClosed() { MESOSPHERE_ASSERT_THIS(); + + this->CleanupRequests(); } Result KLightServerSession::OnRequest(KThread *request_thread) { - MESOSPHERE_UNIMPLEMENTED(); + MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + + /* Check that the server isn't closed. */ + R_UNLESS(!this->parent->IsServerClosed(), svc::ResultSessionClosed()); + + /* Try to sleep the thread. */ + R_UNLESS(this->request_queue.SleepThread(request_thread), svc::ResultTerminationRequested()); + + /* If we don't have a current request, wake up a server thread to handle it. */ + if (this->current_request == nullptr) { + this->server_queue.WakeupFrontThread(); + } + + return ResultSuccess(); } Result KLightServerSession::ReplyAndReceive(u32 *data) { - MESOSPHERE_UNIMPLEMENTED(); + MESOSPHERE_ASSERT_THIS(); + + /* Set the server context. */ + KThread *server_thread = GetCurrentThreadPointer(); + server_thread->SetLightSessionData(data); + + /* Reply, if we need to. */ + KThread *cur_request = nullptr; + if (data[0] & KLightSession::ReplyFlag) { + KScopedSchedulerLock sl; + + /* Check that we're open. */ + R_UNLESS(!this->parent->IsClientClosed(), svc::ResultSessionClosed()); + R_UNLESS(!this->parent->IsServerClosed(), svc::ResultSessionClosed()); + + /* Check that we have a request to reply to. */ + R_UNLESS(this->current_request != nullptr, svc::ResultInvalidState()); + + /* Check that the server thread is correct. */ + R_UNLESS(this->server_thread == server_thread, svc::ResultInvalidState()); + + /* If we can reply, do so. */ + if (!this->current_request->IsTerminationRequested()) { + MESOSPHERE_ASSERT(this->current_request->GetState() == KThread::ThreadState_Waiting); + MESOSPHERE_ASSERT(this->current_request == this->request_queue.GetFront()); + std::memcpy(this->current_request->GetLightSessionData(), server_thread->GetLightSessionData(), KLightSession::DataSize); + this->request_queue.WakeupThread(this->current_request); + } + + /* Clear our current request. */ + cur_request = this->current_request; + this->current_request = nullptr; + this->server_thread = nullptr; + } + + /* Close the current request, if we had one. */ + if (cur_request != nullptr) { + cur_request->Close(); + } + + /* Receive. */ + bool set_cancellable = false; + while (true) { + KScopedSchedulerLock sl; + + /* Check that we aren't already receiving. */ + R_UNLESS(this->server_queue.IsEmpty(), svc::ResultInvalidState()); + R_UNLESS(this->server_thread == nullptr, svc::ResultInvalidState()); + + /* If we cancelled in a previous loop, clear cancel state. */ + if (set_cancellable) { + server_thread->ClearCancellable(); + set_cancellable = false; + } + + /* Check that we're open. */ + R_UNLESS(!this->parent->IsClientClosed(), svc::ResultSessionClosed()); + R_UNLESS(!this->parent->IsServerClosed(), svc::ResultSessionClosed()); + + /* If we have a request available, use it. */ + if (this->current_request == nullptr && this->request_queue.IsEmpty()) { + this->current_request = this->request_queue.GetFront(); + this->current_request->Open(); + this->server_thread = server_thread; + } else { + /* Otherwise, wait for a request to come in. */ + R_UNLESS(this->server_queue.SleepThread(server_thread), svc::ResultTerminationRequested()); + + /* Check if we were cancelled. */ + if (server_thread->IsWaitCancelled()) { + this->server_queue.WakeupThread(server_thread); + server_thread->ClearWaitCancelled(); + return svc::ResultCancelled(); + } + + /* Otherwise, mark as cancellable. */ + server_thread->SetCancellable(); + set_cancellable = true; + } + } + + /* Copy the client data. */ + std::memcpy(server_thread->GetLightSessionData(), this->current_request->GetLightSessionData(), KLightSession::DataSize); } void KLightServerSession::CleanupRequests() { - MESOSPHERE_UNIMPLEMENTED(); + /* Cleanup all pending requests. */ + KThread *cur_request = nullptr; + { + KScopedSchedulerLock sl; + + /* Handle the current request. */ + if (this->current_request != nullptr) { + /* Reply to the current request. */ + if (!this->current_request->IsTerminationRequested()) { + MESOSPHERE_ASSERT(this->current_request->GetState() == KThread::ThreadState_Waiting); + MESOSPHERE_ASSERT(this->current_request == this->request_queue.GetFront()); + this->request_queue.WakeupThread(this->current_request); + this->current_request->SetSyncedObject(nullptr, svc::ResultSessionClosed()); + } + + /* Clear our current request. */ + cur_request = this->current_request; + this->current_request = nullptr; + this->server_thread = nullptr; + } + + /* Reply to all other requests. */ + while (!this->request_queue.IsEmpty()) { + KThread *client_thread = this->request_queue.WakeupFrontThread(); + client_thread->SetSyncedObject(nullptr, svc::ResultSessionClosed()); + } + + /* Wake up all server threads. */ + while (!this->server_queue.IsEmpty()) { + this->server_queue.WakeupFrontThread(); + } + } + + /* Close the current request, if we had one. */ + if (cur_request != nullptr) { + cur_request->Close(); + } } } diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index 45c6b11ce..d12637b64 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -720,8 +720,8 @@ namespace ams::kern { /* Check if we're waiting and cancellable. */ if (this->GetState() == ThreadState_Waiting && this->cancellable) { if (this->sleeping_queue != nullptr) { - /* TODO: Cancel light IPC. */ - MESOSPHERE_UNIMPLEMENTED(); + this->sleeping_queue->WakeupThread(this); + this->wait_cancelled = true; } else { this->SetSyncedObject(nullptr, svc::ResultCancelled()); this->SetState(ThreadState_Runnable); diff --git a/libraries/libmesosphere/source/svc/kern_svc_light_ipc.cpp b/libraries/libmesosphere/source/svc/kern_svc_light_ipc.cpp index 8e80e3932..6796e674e 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_light_ipc.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_light_ipc.cpp @@ -21,28 +21,48 @@ namespace ams::kern::svc { namespace { + ALWAYS_INLINE Result SendSyncRequestLight(ams::svc::Handle session_handle, u32 *args) { + /* Get the light client session from its handle. */ + KScopedAutoObject session = GetCurrentProcess().GetHandleTable().GetObject(session_handle); + R_UNLESS(session.IsNotNull(), svc::ResultInvalidHandle()); + /* Send the request. */ + R_TRY(session->SendSyncRequest(args)); + + return ResultSuccess(); + } + + ALWAYS_INLINE Result ReplyAndReceiveLight(ams::svc::Handle session_handle, u32 *args) { + /* Get the light server session from its handle. */ + KScopedAutoObject session = GetCurrentProcess().GetHandleTable().GetObject(session_handle); + R_UNLESS(session.IsNotNull(), svc::ResultInvalidHandle()); + + /* Handle the request. */ + R_TRY(session->ReplyAndReceive(args)); + + return ResultSuccess(); + } } /* ============================= 64 ABI ============================= */ - Result SendSyncRequestLight64(ams::svc::Handle session_handle) { - MESOSPHERE_PANIC("Stubbed SvcSendSyncRequestLight64 was called."); + Result SendSyncRequestLight64(ams::svc::Handle session_handle, u32 *args) { + return SendSyncRequestLight(session_handle, args); } - Result ReplyAndReceiveLight64(ams::svc::Handle handle) { - MESOSPHERE_PANIC("Stubbed SvcReplyAndReceiveLight64 was called."); + Result ReplyAndReceiveLight64(ams::svc::Handle session_handle, u32 *args) { + return ReplyAndReceiveLight(session_handle, args); } /* ============================= 64From32 ABI ============================= */ - Result SendSyncRequestLight64From32(ams::svc::Handle session_handle) { - MESOSPHERE_PANIC("Stubbed SvcSendSyncRequestLight64From32 was called."); + Result SendSyncRequestLight64From32(ams::svc::Handle session_handle, u32 *args) { + return SendSyncRequestLight(session_handle, args); } - Result ReplyAndReceiveLight64From32(ams::svc::Handle handle) { - MESOSPHERE_PANIC("Stubbed SvcReplyAndReceiveLight64From32 was called."); + Result ReplyAndReceiveLight64From32(ams::svc::Handle session_handle, u32 *args) { + return ReplyAndReceiveLight(session_handle, args); } }