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);
}
}