18 changed files with 813 additions and 20 deletions
-
6src/core/CMakeLists.txt
-
69src/core/hle/kernel/k_client_port.cpp
-
2src/core/hle/kernel/k_client_port.h
-
31src/core/hle/kernel/k_light_client_session.cpp
-
39src/core/hle/kernel/k_light_client_session.h
-
247src/core/hle/kernel/k_light_server_session.cpp
-
49src/core/hle/kernel/k_light_server_session.h
-
81src/core/hle/kernel/k_light_session.cpp
-
86src/core/hle/kernel/k_light_session.h
-
9src/core/hle/kernel/k_port.cpp
-
2src/core/hle/kernel/k_port.h
-
56src/core/hle/kernel/k_server_port.cpp
-
5src/core/hle/kernel/k_server_port.h
-
7src/core/hle/kernel/k_thread.h
-
4src/core/hle/kernel/kernel.cpp
-
29src/core/hle/kernel/svc/svc_light_ipc.cpp
-
71src/core/hle/kernel/svc/svc_port.cpp
-
40src/core/hle/kernel/svc/svc_session.cpp
@ -0,0 +1,31 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include "core/hle/kernel/k_light_client_session.h"
|
|||
#include "core/hle/kernel/k_light_session.h"
|
|||
#include "core/hle/kernel/k_thread.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
KLightClientSession::KLightClientSession(KernelCore& kernel) : KAutoObject(kernel) {} |
|||
|
|||
KLightClientSession::~KLightClientSession() = default; |
|||
|
|||
void KLightClientSession::Destroy() { |
|||
m_parent->OnClientClosed(); |
|||
} |
|||
|
|||
void KLightClientSession::OnServerClosed() {} |
|||
|
|||
Result KLightClientSession::SendSyncRequest(u32* data) { |
|||
// Get the request thread.
|
|||
KThread* cur_thread = GetCurrentThreadPointer(m_kernel); |
|||
|
|||
// Set the light data.
|
|||
cur_thread->SetLightSessionData(data); |
|||
|
|||
// Send the request.
|
|||
R_RETURN(m_parent->OnRequest(cur_thread)); |
|||
} |
|||
|
|||
} // namespace Kernel
|
|||
@ -0,0 +1,39 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "core/hle/kernel/k_auto_object.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace Kernel { |
|||
|
|||
class KLightSession; |
|||
|
|||
class KLightClientSession final : public KAutoObject { |
|||
KERNEL_AUTOOBJECT_TRAITS(KLightClientSession, KAutoObject); |
|||
|
|||
public: |
|||
explicit KLightClientSession(KernelCore& kernel); |
|||
~KLightClientSession(); |
|||
|
|||
void Initialize(KLightSession* parent) { |
|||
// Set member variables. |
|||
m_parent = parent; |
|||
} |
|||
|
|||
virtual void Destroy() override; |
|||
|
|||
const KLightSession* GetParent() const { |
|||
return m_parent; |
|||
} |
|||
|
|||
Result SendSyncRequest(u32* data); |
|||
|
|||
void OnServerClosed(); |
|||
|
|||
private: |
|||
KLightSession* m_parent; |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
@ -0,0 +1,247 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include "core/hle/kernel/k_light_server_session.h"
|
|||
#include "core/hle/kernel/k_light_session.h"
|
|||
#include "core/hle/kernel/k_thread.h"
|
|||
#include "core/hle/kernel/k_thread_queue.h"
|
|||
#include "core/hle/kernel/svc_results.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
namespace { |
|||
|
|||
constexpr u64 InvalidThreadId = std::numeric_limits<u64>::max(); |
|||
|
|||
class ThreadQueueImplForKLightServerSessionRequest final : public KThreadQueue { |
|||
private: |
|||
KThread::WaiterList* m_wait_list; |
|||
|
|||
public: |
|||
ThreadQueueImplForKLightServerSessionRequest(KernelCore& kernel, KThread::WaiterList* wl) |
|||
: KThreadQueue(kernel), m_wait_list(wl) {} |
|||
|
|||
virtual void EndWait(KThread* waiting_thread, Result wait_result) override { |
|||
// Remove the thread from our wait list.
|
|||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); |
|||
|
|||
// Invoke the base end wait handler.
|
|||
KThreadQueue::EndWait(waiting_thread, wait_result); |
|||
} |
|||
|
|||
virtual void CancelWait(KThread* waiting_thread, Result wait_result, |
|||
bool cancel_timer_task) override { |
|||
// Remove the thread from our wait list.
|
|||
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread)); |
|||
|
|||
// Invoke the base cancel wait handler.
|
|||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); |
|||
} |
|||
}; |
|||
|
|||
class ThreadQueueImplForKLightServerSessionReceive final : public KThreadQueue { |
|||
private: |
|||
KThread** m_server_thread; |
|||
|
|||
public: |
|||
ThreadQueueImplForKLightServerSessionReceive(KernelCore& kernel, KThread** st) |
|||
: KThreadQueue(kernel), m_server_thread(st) {} |
|||
|
|||
virtual void EndWait(KThread* waiting_thread, Result wait_result) override { |
|||
// Clear the server thread.
|
|||
*m_server_thread = nullptr; |
|||
|
|||
// Set the waiting thread as not cancelable.
|
|||
waiting_thread->ClearCancellable(); |
|||
|
|||
// Invoke the base end wait handler.
|
|||
KThreadQueue::EndWait(waiting_thread, wait_result); |
|||
} |
|||
|
|||
virtual void CancelWait(KThread* waiting_thread, Result wait_result, |
|||
bool cancel_timer_task) override { |
|||
// Clear the server thread.
|
|||
*m_server_thread = nullptr; |
|||
|
|||
// Set the waiting thread as not cancelable.
|
|||
waiting_thread->ClearCancellable(); |
|||
|
|||
// Invoke the base cancel wait handler.
|
|||
KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task); |
|||
} |
|||
}; |
|||
|
|||
} // namespace
|
|||
|
|||
KLightServerSession::KLightServerSession(KernelCore& kernel) : KAutoObject(kernel) {} |
|||
KLightServerSession::~KLightServerSession() = default; |
|||
|
|||
void KLightServerSession::Destroy() { |
|||
this->CleanupRequests(); |
|||
|
|||
m_parent->OnServerClosed(); |
|||
} |
|||
|
|||
void KLightServerSession::OnClientClosed() { |
|||
this->CleanupRequests(); |
|||
} |
|||
|
|||
Result KLightServerSession::OnRequest(KThread* request_thread) { |
|||
ThreadQueueImplForKLightServerSessionRequest wait_queue(m_kernel, |
|||
std::addressof(m_request_list)); |
|||
|
|||
// Send the request.
|
|||
{ |
|||
// Lock the scheduler.
|
|||
KScopedSchedulerLock sl(m_kernel); |
|||
|
|||
// Check that the server isn't closed.
|
|||
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed); |
|||
|
|||
// Check that the request thread isn't terminating.
|
|||
R_UNLESS(!request_thread->IsTerminationRequested(), ResultTerminationRequested); |
|||
|
|||
// Add the request thread to our list.
|
|||
m_request_list.push_back(*request_thread); |
|||
|
|||
// Begin waiting on the request.
|
|||
request_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); |
|||
request_thread->BeginWait(std::addressof(wait_queue)); |
|||
|
|||
// If we have a server thread, end its wait.
|
|||
if (m_server_thread != nullptr) { |
|||
m_server_thread->EndWait(ResultSuccess); |
|||
} |
|||
} |
|||
|
|||
// NOTE: Nintendo returns GetCurrentThread().GetWaitResult() here.
|
|||
// This is technically incorrect, although it doesn't cause problems in practice
|
|||
// because this is only ever called with request_thread = GetCurrentThreadPointer().
|
|||
R_RETURN(request_thread->GetWaitResult()); |
|||
} |
|||
|
|||
Result KLightServerSession::ReplyAndReceive(u32* data) { |
|||
// Set the server context.
|
|||
GetCurrentThread(m_kernel).SetLightSessionData(data); |
|||
|
|||
// Reply, if we need to.
|
|||
if (data[0] & KLightSession::ReplyFlag) { |
|||
KScopedSchedulerLock sl(m_kernel); |
|||
|
|||
// Check that we're open.
|
|||
R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed); |
|||
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed); |
|||
|
|||
// Check that we have a request to reply to.
|
|||
R_UNLESS(m_current_request != nullptr, ResultInvalidState); |
|||
|
|||
// Check that the server thread id is correct.
|
|||
R_UNLESS(m_server_thread_id == GetCurrentThread(m_kernel).GetId(), ResultInvalidState); |
|||
|
|||
// If we can reply, do so.
|
|||
if (!m_current_request->IsTerminationRequested()) { |
|||
std::memcpy(m_current_request->GetLightSessionData(), |
|||
GetCurrentThread(m_kernel).GetLightSessionData(), KLightSession::DataSize); |
|||
m_current_request->EndWait(ResultSuccess); |
|||
} |
|||
|
|||
// Close our current request.
|
|||
m_current_request->Close(); |
|||
|
|||
// Clear our current request.
|
|||
m_current_request = nullptr; |
|||
m_server_thread_id = InvalidThreadId; |
|||
} |
|||
|
|||
// Create the wait queue for our receive.
|
|||
ThreadQueueImplForKLightServerSessionReceive wait_queue(m_kernel, |
|||
std::addressof(m_server_thread)); |
|||
|
|||
// Receive.
|
|||
while (true) { |
|||
// Try to receive a request.
|
|||
{ |
|||
KScopedSchedulerLock sl(m_kernel); |
|||
|
|||
// Check that we aren't already receiving.
|
|||
R_UNLESS(m_server_thread == nullptr, ResultInvalidState); |
|||
R_UNLESS(m_server_thread_id == InvalidThreadId, ResultInvalidState); |
|||
|
|||
// Check that we're open.
|
|||
R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed); |
|||
R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed); |
|||
|
|||
// Check that we're not terminating.
|
|||
R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(), |
|||
ResultTerminationRequested); |
|||
|
|||
// If we have a request available, use it.
|
|||
if (auto head = m_request_list.begin(); head != m_request_list.end()) { |
|||
// Set our current request.
|
|||
m_current_request = std::addressof(*head); |
|||
m_current_request->Open(); |
|||
|
|||
// Set our server thread id.
|
|||
m_server_thread_id = GetCurrentThread(m_kernel).GetId(); |
|||
|
|||
// Copy the client request data.
|
|||
std::memcpy(GetCurrentThread(m_kernel).GetLightSessionData(), |
|||
m_current_request->GetLightSessionData(), KLightSession::DataSize); |
|||
|
|||
// We successfully received.
|
|||
R_SUCCEED(); |
|||
} |
|||
|
|||
// We need to wait for a request to come in.
|
|||
|
|||
// Check if we were cancelled.
|
|||
if (GetCurrentThread(m_kernel).IsWaitCancelled()) { |
|||
GetCurrentThread(m_kernel).ClearWaitCancelled(); |
|||
R_THROW(ResultCancelled); |
|||
} |
|||
|
|||
// Mark ourselves as cancellable.
|
|||
GetCurrentThread(m_kernel).SetCancellable(); |
|||
|
|||
// Wait for a request to come in.
|
|||
m_server_thread = GetCurrentThreadPointer(m_kernel); |
|||
GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); |
|||
GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue)); |
|||
} |
|||
|
|||
// We waited to receive a request; if our wait failed, return the failing result.
|
|||
R_TRY(GetCurrentThread(m_kernel).GetWaitResult()); |
|||
} |
|||
} |
|||
|
|||
void KLightServerSession::CleanupRequests() { |
|||
// Cleanup all pending requests.
|
|||
{ |
|||
KScopedSchedulerLock sl(m_kernel); |
|||
|
|||
// Handle the current request.
|
|||
if (m_current_request != nullptr) { |
|||
// Reply to the current request.
|
|||
if (!m_current_request->IsTerminationRequested()) { |
|||
m_current_request->EndWait(ResultSessionClosed); |
|||
} |
|||
|
|||
// Clear our current request.
|
|||
m_current_request->Close(); |
|||
m_current_request = nullptr; |
|||
m_server_thread_id = InvalidThreadId; |
|||
} |
|||
|
|||
// Reply to all other requests.
|
|||
for (auto& thread : m_request_list) { |
|||
thread.EndWait(ResultSessionClosed); |
|||
} |
|||
|
|||
// Wait up our server thread, if we have one.
|
|||
if (m_server_thread != nullptr) { |
|||
m_server_thread->EndWait(ResultSessionClosed); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} // namespace Kernel
|
|||
@ -0,0 +1,49 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "core/hle/kernel/k_auto_object.h" |
|||
#include "core/hle/kernel/k_thread.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace Kernel { |
|||
|
|||
class KLightSession; |
|||
|
|||
class KLightServerSession final : public KAutoObject, |
|||
public Common::IntrusiveListBaseNode<KLightServerSession> { |
|||
KERNEL_AUTOOBJECT_TRAITS(KLightServerSession, KAutoObject); |
|||
|
|||
private: |
|||
KLightSession* m_parent{}; |
|||
KThread::WaiterList m_request_list{}; |
|||
KThread* m_current_request{}; |
|||
u64 m_server_thread_id{std::numeric_limits<u64>::max()}; |
|||
KThread* m_server_thread{}; |
|||
|
|||
public: |
|||
explicit KLightServerSession(KernelCore& kernel); |
|||
~KLightServerSession(); |
|||
|
|||
void Initialize(KLightSession* parent) { |
|||
// Set member variables. */ |
|||
m_parent = parent; |
|||
} |
|||
|
|||
virtual void Destroy() override; |
|||
|
|||
constexpr const KLightSession* GetParent() const { |
|||
return m_parent; |
|||
} |
|||
|
|||
Result OnRequest(KThread* request_thread); |
|||
Result ReplyAndReceive(u32* data); |
|||
|
|||
void OnClientClosed(); |
|||
|
|||
private: |
|||
void CleanupRequests(); |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
@ -0,0 +1,81 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|||
|
|||
#include "core/hle/kernel/k_client_port.h"
|
|||
#include "core/hle/kernel/k_light_client_session.h"
|
|||
#include "core/hle/kernel/k_light_server_session.h"
|
|||
#include "core/hle/kernel/k_light_session.h"
|
|||
#include "core/hle/kernel/k_process.h"
|
|||
|
|||
namespace Kernel { |
|||
|
|||
KLightSession::KLightSession(KernelCore& kernel) |
|||
: KAutoObjectWithSlabHeapAndContainer(kernel), m_server(kernel), m_client(kernel) {} |
|||
KLightSession::~KLightSession() = default; |
|||
|
|||
void KLightSession::Initialize(KClientPort* client_port, uintptr_t name) { |
|||
// Increment reference count.
|
|||
// Because reference count is one on creation, this will result
|
|||
// in a reference count of two. Thus, when both server and client are closed
|
|||
// this object will be destroyed.
|
|||
this->Open(); |
|||
|
|||
// Create our sub sessions.
|
|||
KAutoObject::Create(std::addressof(m_server)); |
|||
KAutoObject::Create(std::addressof(m_client)); |
|||
|
|||
// Initialize our sub sessions.
|
|||
m_server.Initialize(this); |
|||
m_client.Initialize(this); |
|||
|
|||
// Set state and name.
|
|||
m_state = State::Normal; |
|||
m_name = name; |
|||
|
|||
// Set our owner process.
|
|||
m_process = GetCurrentProcessPointer(m_kernel); |
|||
m_process->Open(); |
|||
|
|||
// Set our port.
|
|||
m_port = client_port; |
|||
if (m_port != nullptr) { |
|||
m_port->Open(); |
|||
} |
|||
|
|||
// Mark initialized.
|
|||
m_initialized = true; |
|||
} |
|||
|
|||
void KLightSession::Finalize() { |
|||
if (m_port != nullptr) { |
|||
m_port->OnSessionFinalized(); |
|||
m_port->Close(); |
|||
} |
|||
} |
|||
|
|||
void KLightSession::OnServerClosed() { |
|||
if (m_state == State::Normal) { |
|||
m_state = State::ServerClosed; |
|||
m_client.OnServerClosed(); |
|||
} |
|||
|
|||
this->Close(); |
|||
} |
|||
|
|||
void KLightSession::OnClientClosed() { |
|||
if (m_state == State::Normal) { |
|||
m_state = State::ClientClosed; |
|||
m_server.OnClientClosed(); |
|||
} |
|||
|
|||
this->Close(); |
|||
} |
|||
|
|||
void KLightSession::PostDestroy(uintptr_t arg) { |
|||
// Release the session count resource the owner process holds.
|
|||
KProcess* owner = reinterpret_cast<KProcess*>(arg); |
|||
owner->ReleaseResource(Svc::LimitableResource::SessionCountMax, 1); |
|||
owner->Close(); |
|||
} |
|||
|
|||
} // namespace Kernel
|
|||
@ -0,0 +1,86 @@ |
|||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
|||
// SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
#pragma once |
|||
|
|||
#include "core/hle/kernel/k_light_client_session.h" |
|||
#include "core/hle/kernel/k_light_server_session.h" |
|||
#include "core/hle/kernel/slab_helpers.h" |
|||
#include "core/hle/result.h" |
|||
|
|||
namespace Kernel { |
|||
|
|||
class KClientPort; |
|||
class KProcess; |
|||
|
|||
// TODO: SupportDynamicExpansion for SlabHeap |
|||
class KLightSession final |
|||
: public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> { |
|||
KERNEL_AUTOOBJECT_TRAITS(KLightSession, KAutoObject); |
|||
|
|||
private: |
|||
enum class State : u8 { |
|||
Invalid = 0, |
|||
Normal = 1, |
|||
ClientClosed = 2, |
|||
ServerClosed = 3, |
|||
}; |
|||
|
|||
public: |
|||
static constexpr size_t DataSize = sizeof(u32) * 7; |
|||
static constexpr u32 ReplyFlag = (1U << 31); |
|||
|
|||
private: |
|||
KLightServerSession m_server; |
|||
KLightClientSession m_client; |
|||
State m_state{State::Invalid}; |
|||
KClientPort* m_port{}; |
|||
uintptr_t m_name{}; |
|||
KProcess* m_process{}; |
|||
bool m_initialized{}; |
|||
|
|||
public: |
|||
explicit KLightSession(KernelCore& kernel); |
|||
~KLightSession(); |
|||
|
|||
void Initialize(KClientPort* client_port, uintptr_t name); |
|||
void Finalize() override; |
|||
|
|||
bool IsInitialized() const override { |
|||
return m_initialized; |
|||
} |
|||
uintptr_t GetPostDestroyArgument() const override { |
|||
return reinterpret_cast<uintptr_t>(m_process); |
|||
} |
|||
|
|||
static void PostDestroy(uintptr_t arg); |
|||
|
|||
void OnServerClosed(); |
|||
void OnClientClosed(); |
|||
|
|||
bool IsServerClosed() const { |
|||
return m_state != State::Normal; |
|||
} |
|||
bool IsClientClosed() const { |
|||
return m_state != State::Normal; |
|||
} |
|||
|
|||
Result OnRequest(KThread* request_thread) { |
|||
R_RETURN(m_server.OnRequest(request_thread)); |
|||
} |
|||
|
|||
KLightClientSession& GetClientSession() { |
|||
return m_client; |
|||
} |
|||
KLightServerSession& GetServerSession() { |
|||
return m_server; |
|||
} |
|||
const KLightClientSession& GetClientSession() const { |
|||
return m_client; |
|||
} |
|||
const KLightServerSession& GetServerSession() const { |
|||
return m_server; |
|||
} |
|||
}; |
|||
|
|||
} // namespace Kernel |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue