Merge pull request #9078 from liamwhite/session-request

kernel: Session request cleanup
master
liamwhite 2022-10-20 18:07:30 +07:00 committed by GitHub
commit a6628e8dba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 607 additions and 199 deletions

@ -243,6 +243,8 @@ add_library(core STATIC
hle/kernel/k_server_session.h hle/kernel/k_server_session.h
hle/kernel/k_session.cpp hle/kernel/k_session.cpp
hle/kernel/k_session.h hle/kernel/k_session.h
hle/kernel/k_session_request.cpp
hle/kernel/k_session_request.h
hle/kernel/k_shared_memory.cpp hle/kernel/k_shared_memory.cpp
hle/kernel/k_shared_memory.h hle/kernel/k_shared_memory.h
hle/kernel/k_shared_memory_info.h hle/kernel/k_shared_memory_info.h

@ -86,13 +86,13 @@ public:
u32 num_domain_objects{}; u32 num_domain_objects{};
const bool always_move_handles{ const bool always_move_handles{
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0}; (static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
if (!ctx.Session()->IsDomain() || always_move_handles) { if (!ctx.Session()->GetSessionRequestManager()->IsDomain() || always_move_handles) {
num_handles_to_move = num_objects_to_move; num_handles_to_move = num_objects_to_move;
} else { } else {
num_domain_objects = num_objects_to_move; num_domain_objects = num_objects_to_move;
} }
if (ctx.Session()->IsDomain()) { if (ctx.Session()->GetSessionRequestManager()->IsDomain()) {
raw_data_size += raw_data_size +=
static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects); static_cast<u32>(sizeof(DomainMessageHeader) / sizeof(u32) + num_domain_objects);
ctx.write_size += num_domain_objects; ctx.write_size += num_domain_objects;
@ -125,7 +125,8 @@ public:
if (!ctx.IsTipc()) { if (!ctx.IsTipc()) {
AlignWithPadding(); AlignWithPadding();
if (ctx.Session()->IsDomain() && ctx.HasDomainMessageHeader()) { if (ctx.Session()->GetSessionRequestManager()->IsDomain() &&
ctx.HasDomainMessageHeader()) {
IPC::DomainMessageHeader domain_header{}; IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects; domain_header.num_objects = num_domain_objects;
PushRaw(domain_header); PushRaw(domain_header);
@ -145,7 +146,7 @@ public:
template <class T> template <class T>
void PushIpcInterface(std::shared_ptr<T> iface) { void PushIpcInterface(std::shared_ptr<T> iface) {
if (context->Session()->IsDomain()) { if (context->Session()->GetSessionRequestManager()->IsDomain()) {
context->AddDomainObject(std::move(iface)); context->AddDomainObject(std::move(iface));
} else { } else {
kernel.CurrentProcess()->GetResourceLimit()->Reserve( kernel.CurrentProcess()->GetResourceLimit()->Reserve(
@ -386,7 +387,7 @@ public:
template <class T> template <class T>
std::weak_ptr<T> PopIpcInterface() { std::weak_ptr<T> PopIpcInterface() {
ASSERT(context->Session()->IsDomain()); ASSERT(context->Session()->GetSessionRequestManager()->IsDomain());
ASSERT(context->GetDomainMessageHeader().input_object_count > 0); ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainHandler<T>(Pop<u32>() - 1); return context->GetDomainHandler<T>(Pop<u32>() - 1);
} }

@ -19,6 +19,7 @@
#include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/service_thread.h"
#include "core/memory.h" #include "core/memory.h"
namespace Kernel { namespace Kernel {
@ -56,16 +57,103 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
} }
} }
Result SessionRequestManager::CompleteSyncRequest(KServerSession* server_session,
HLERequestContext& context) {
Result result = ResultSuccess;
// If the session has been converted to a domain, handle the domain request
if (this->HasSessionRequestHandler(context)) {
if (IsDomain() && context.HasDomainMessageHeader()) {
result = HandleDomainSyncRequest(server_session, context);
// If there is no domain header, the regular session handler is used
} else if (this->HasSessionHandler()) {
// If this manager has an associated HLE handler, forward the request to it.
result = this->SessionHandler().HandleSyncRequest(*server_session, context);
}
} else {
ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
IPC::ResponseBuilder rb(context, 2);
rb.Push(ResultSuccess);
}
if (convert_to_domain) {
ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance.");
this->ConvertToDomain();
convert_to_domain = false;
}
return result;
}
Result SessionRequestManager::HandleDomainSyncRequest(KServerSession* server_session,
HLERequestContext& context) {
if (!context.HasDomainMessageHeader()) {
return ResultSuccess;
}
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
context.SetSessionRequestManager(server_session->GetSessionRequestManager());
// If there is a DomainMessageHeader, then this is CommandType "Request"
const auto& domain_message_header = context.GetDomainMessageHeader();
const u32 object_id{domain_message_header.object_id};
switch (domain_message_header.command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
if (object_id > this->DomainHandlerCount()) {
LOG_CRITICAL(IPC,
"object_id {} is too big! This probably means a recent service call "
"needed to return a new interface!",
object_id);
ASSERT(false);
return ResultSuccess; // Ignore error if asserts are off
}
if (auto strong_ptr = this->DomainHandler(object_id - 1).lock()) {
return strong_ptr->HandleSyncRequest(*server_session, context);
} else {
ASSERT(false);
return ResultSuccess;
}
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
this->CloseDomainHandler(object_id - 1);
IPC::ResponseBuilder rb{context, 2};
rb.Push(ResultSuccess);
return ResultSuccess;
}
}
LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value());
ASSERT(false);
return ResultSuccess;
}
Result SessionRequestManager::QueueSyncRequest(KSession* parent,
std::shared_ptr<HLERequestContext>&& context) {
// Ensure we have a session request handler
if (this->HasSessionRequestHandler(*context)) {
if (auto strong_ptr = this->GetServiceThread().lock()) {
strong_ptr->QueueSyncRequest(*parent, std::move(context));
} else {
ASSERT_MSG(false, "strong_ptr is nullptr!");
}
} else {
ASSERT_MSG(false, "handler is invalid!");
}
return ResultSuccess;
}
void SessionRequestHandler::ClientConnected(KServerSession* session) { void SessionRequestHandler::ClientConnected(KServerSession* session) {
session->ClientConnected(shared_from_this()); session->GetSessionRequestManager()->SetSessionHandler(shared_from_this());
// Ensure our server session is tracked globally. // Ensure our server session is tracked globally.
kernel.RegisterServerObject(session); kernel.RegisterServerObject(session);
} }
void SessionRequestHandler::ClientDisconnected(KServerSession* session) { void SessionRequestHandler::ClientDisconnected(KServerSession* session) {}
session->ClientDisconnected();
}
HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_, HLERequestContext::HLERequestContext(KernelCore& kernel_, Core::Memory::Memory& memory_,
KServerSession* server_session_, KThread* thread_) KServerSession* server_session_, KThread* thread_)
@ -126,7 +214,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
// Padding to align to 16 bytes // Padding to align to 16 bytes
rp.AlignWithPadding(); rp.AlignWithPadding();
if (Session()->IsDomain() && if (Session()->GetSessionRequestManager()->IsDomain() &&
((command_header->type == IPC::CommandType::Request || ((command_header->type == IPC::CommandType::Request ||
command_header->type == IPC::CommandType::RequestWithContext) || command_header->type == IPC::CommandType::RequestWithContext) ||
!incoming)) { !incoming)) {
@ -135,7 +223,7 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
if (incoming || domain_message_header) { if (incoming || domain_message_header) {
domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>(); domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
} else { } else {
if (Session()->IsDomain()) { if (Session()->GetSessionRequestManager()->IsDomain()) {
LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
} }
} }
@ -228,12 +316,12 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa
// Write the domain objects to the command buffer, these go after the raw untranslated data. // Write the domain objects to the command buffer, these go after the raw untranslated data.
// TODO(Subv): This completely ignores C buffers. // TODO(Subv): This completely ignores C buffers.
if (Session()->IsDomain()) { if (server_session->GetSessionRequestManager()->IsDomain()) {
current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size()); current_offset = domain_offset - static_cast<u32>(outgoing_domain_objects.size());
for (const auto& object : outgoing_domain_objects) { for (auto& object : outgoing_domain_objects) {
server_session->AppendDomainHandler(object); server_session->GetSessionRequestManager()->AppendDomainHandler(std::move(object));
cmd_buf[current_offset++] = cmd_buf[current_offset++] = static_cast<u32_le>(
static_cast<u32_le>(server_session->NumDomainRequestHandlers()); server_session->GetSessionRequestManager()->DomainHandlerCount());
} }
} }

@ -121,6 +121,10 @@ public:
is_domain = true; is_domain = true;
} }
void ConvertToDomainOnRequestEnd() {
convert_to_domain = true;
}
std::size_t DomainHandlerCount() const { std::size_t DomainHandlerCount() const {
return domain_handlers.size(); return domain_handlers.size();
} }
@ -164,7 +168,12 @@ public:
bool HasSessionRequestHandler(const HLERequestContext& context) const; bool HasSessionRequestHandler(const HLERequestContext& context) const;
Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
Result QueueSyncRequest(KSession* parent, std::shared_ptr<HLERequestContext>&& context);
private: private:
bool convert_to_domain{};
bool is_domain{}; bool is_domain{};
SessionRequestHandlerPtr session_handler; SessionRequestHandlerPtr session_handler;
std::vector<SessionRequestHandlerPtr> domain_handlers; std::vector<SessionRequestHandlerPtr> domain_handlers;

@ -18,6 +18,7 @@
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h" #include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h" #include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_session_request.h"
#include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h" #include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_system_control.h" #include "core/hle/kernel/k_system_control.h"
@ -34,6 +35,7 @@ namespace Kernel::Init {
HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \ HANDLER(KThread, (SLAB_COUNT(KThread)), ##__VA_ARGS__) \
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSessionRequest, (SLAB_COUNT(KSession) * 2), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_session.h" #include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_server_session.h" #include "core/hle/kernel/k_server_session.h"
@ -10,6 +11,8 @@
namespace Kernel { namespace Kernel {
static constexpr u32 MessageBufferSize = 0x100;
KClientSession::KClientSession(KernelCore& kernel_) KClientSession::KClientSession(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_} {} : KAutoObjectWithSlabHeapAndContainer{kernel_} {}
KClientSession::~KClientSession() = default; KClientSession::~KClientSession() = default;
@ -22,8 +25,16 @@ void KClientSession::Destroy() {
void KClientSession::OnServerClosed() {} void KClientSession::OnServerClosed() {}
Result KClientSession::SendSyncRequest() { Result KClientSession::SendSyncRequest() {
// Signal the server session that new data is available // Create a session request.
return parent->GetServerSession().OnRequest(); KSessionRequest* request = KSessionRequest::Create(kernel);
R_UNLESS(request != nullptr, ResultOutOfResource);
SCOPE_EXIT({ request->Close(); });
// Initialize the request.
request->Initialize(nullptr, GetCurrentThread(kernel).GetTLSAddress(), MessageBufferSize);
// Send the request.
return parent->GetServerSession().OnRequest(request);
} }
} // namespace Kernel } // namespace Kernel

@ -16,6 +16,7 @@ class KLinkedListNode : public boost::intrusive::list_base_hook<>,
public KSlabAllocated<KLinkedListNode> { public KSlabAllocated<KLinkedListNode> {
public: public:
explicit KLinkedListNode(KernelCore&) {}
KLinkedListNode() = default; KLinkedListNode() = default;
void Initialize(void* it) { void Initialize(void* it) {

@ -13,6 +13,7 @@ namespace Kernel {
class KPageBuffer final : public KSlabAllocated<KPageBuffer> { class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
public: public:
explicit KPageBuffer(KernelCore&) {}
KPageBuffer() = default; KPageBuffer() = default;
static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr); static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);

@ -22,15 +22,12 @@
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/service_thread.h"
#include "core/memory.h" #include "core/memory.h"
namespace Kernel { namespace Kernel {
using ThreadQueueImplForKServerSessionRequest = KThreadQueue; using ThreadQueueImplForKServerSessionRequest = KThreadQueue;
static constexpr u32 MessageBufferSize = 0x100;
KServerSession::KServerSession(KernelCore& kernel_) KServerSession::KServerSession(KernelCore& kernel_)
: KSynchronizationObject{kernel_}, m_lock{kernel_} {} : KSynchronizationObject{kernel_}, m_lock{kernel_} {}
@ -73,59 +70,7 @@ bool KServerSession::IsSignaled() const {
} }
// Otherwise, we're signaled if we have a request and aren't handling one. // Otherwise, we're signaled if we have a request and aren't handling one.
return !m_thread_request_list.empty() && m_current_thread_request == nullptr; return !m_request_list.empty() && m_current_request == nullptr;
}
void KServerSession::AppendDomainHandler(SessionRequestHandlerPtr handler) {
manager->AppendDomainHandler(std::move(handler));
}
std::size_t KServerSession::NumDomainRequestHandlers() const {
return manager->DomainHandlerCount();
}
Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
if (!context.HasDomainMessageHeader()) {
return ResultSuccess;
}
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
context.SetSessionRequestManager(manager);
// If there is a DomainMessageHeader, then this is CommandType "Request"
const auto& domain_message_header = context.GetDomainMessageHeader();
const u32 object_id{domain_message_header.object_id};
switch (domain_message_header.command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
if (object_id > manager->DomainHandlerCount()) {
LOG_CRITICAL(IPC,
"object_id {} is too big! This probably means a recent service call "
"to {} needed to return a new interface!",
object_id, name);
ASSERT(false);
return ResultSuccess; // Ignore error if asserts are off
}
if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
return strong_ptr->HandleSyncRequest(*this, context);
} else {
ASSERT(false);
return ResultSuccess;
}
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
manager->CloseDomainHandler(object_id - 1);
IPC::ResponseBuilder rb{context, 2};
rb.Push(ResultSuccess);
return ResultSuccess;
}
}
LOG_CRITICAL(IPC, "Unknown domain command={}", domain_message_header.command.Value());
ASSERT(false);
return ResultSuccess;
} }
Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) { Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
@ -134,43 +79,11 @@ Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& m
context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
// Ensure we have a session request handler return manager->QueueSyncRequest(parent, std::move(context));
if (manager->HasSessionRequestHandler(*context)) {
if (auto strong_ptr = manager->GetServiceThread().lock()) {
strong_ptr->QueueSyncRequest(*parent, std::move(context));
} else {
ASSERT_MSG(false, "strong_ptr is nullptr!");
}
} else {
ASSERT_MSG(false, "handler is invalid!");
}
return ResultSuccess;
} }
Result KServerSession::CompleteSyncRequest(HLERequestContext& context) { Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
Result result = ResultSuccess; Result result = manager->CompleteSyncRequest(this, context);
// If the session has been converted to a domain, handle the domain request
if (manager->HasSessionRequestHandler(context)) {
if (IsDomain() && context.HasDomainMessageHeader()) {
result = HandleDomainSyncRequest(context);
// If there is no domain header, the regular session handler is used
} else if (manager->HasSessionHandler()) {
// If this ServerSession has an associated HLE handler, forward the request to it.
result = manager->SessionHandler().HandleSyncRequest(*this, context);
}
} else {
ASSERT_MSG(false, "Session handler is invalid, stubbing response!");
IPC::ResponseBuilder rb(context, 2);
rb.Push(ResultSuccess);
}
if (convert_to_domain) {
ASSERT_MSG(!IsDomain(), "ServerSession is already a domain instance.");
manager->ConvertToDomain();
convert_to_domain = false;
}
// The calling thread is waiting for this request to complete, so wake it up. // The calling thread is waiting for this request to complete, so wake it up.
context.GetThread().EndWait(result); context.GetThread().EndWait(result);
@ -178,7 +91,7 @@ Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
return result; return result;
} }
Result KServerSession::OnRequest() { Result KServerSession::OnRequest(KSessionRequest* request) {
// Create the wait queue. // Create the wait queue.
ThreadQueueImplForKServerSessionRequest wait_queue{kernel}; ThreadQueueImplForKServerSessionRequest wait_queue{kernel};
@ -198,14 +111,13 @@ Result KServerSession::OnRequest() {
this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory); this->QueueSyncRequest(GetCurrentThreadPointer(kernel), memory);
} else { } else {
// Non-HLE request. // Non-HLE request.
auto* thread{GetCurrentThreadPointer(kernel)};
// Get whether we're empty. // Get whether we're empty.
const bool was_empty = m_thread_request_list.empty(); const bool was_empty = m_request_list.empty();
// Add the thread to the list. // Add the request to the list.
thread->Open(); request->Open();
m_thread_request_list.push_back(thread); m_request_list.push_back(*request);
// If we were empty, signal. // If we were empty, signal.
if (was_empty) { if (was_empty) {
@ -213,6 +125,9 @@ Result KServerSession::OnRequest() {
} }
} }
// If we have a request event, this is asynchronous, and we don't need to wait.
R_SUCCEED_IF(request->GetEvent() != nullptr);
// This is a synchronous request, so we should wait for our request to complete. // This is a synchronous request, so we should wait for our request to complete.
GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC); GetCurrentThread(kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
GetCurrentThread(kernel).BeginWait(&wait_queue); GetCurrentThread(kernel).BeginWait(&wait_queue);
@ -223,32 +138,32 @@ Result KServerSession::OnRequest() {
Result KServerSession::SendReply() { Result KServerSession::SendReply() {
// Lock the session. // Lock the session.
KScopedLightLock lk(m_lock); KScopedLightLock lk{m_lock};
// Get the request. // Get the request.
KThread* client_thread; KSessionRequest* request;
{ {
KScopedSchedulerLock sl{kernel}; KScopedSchedulerLock sl{kernel};
// Get the current request. // Get the current request.
client_thread = m_current_thread_request; request = m_current_request;
R_UNLESS(client_thread != nullptr, ResultInvalidState); R_UNLESS(request != nullptr, ResultInvalidState);
// Clear the current request, since we're processing it. // Clear the current request, since we're processing it.
m_current_thread_request = nullptr; m_current_request = nullptr;
if (!m_thread_request_list.empty()) { if (!m_request_list.empty()) {
this->NotifyAvailable(); this->NotifyAvailable();
} }
} }
// Close reference to the request once we're done processing it. // Close reference to the request once we're done processing it.
SCOPE_EXIT({ client_thread->Close(); }); SCOPE_EXIT({ request->Close(); });
// Extract relevant information from the request. // Extract relevant information from the request.
// const uintptr_t client_message = request->GetAddress(); const uintptr_t client_message = request->GetAddress();
// const size_t client_buffer_size = request->GetSize(); const size_t client_buffer_size = request->GetSize();
// KThread *client_thread = request->GetThread(); KThread* client_thread = request->GetThread();
// KEvent *event = request->GetEvent(); KEvent* event = request->GetEvent();
// Check whether we're closed. // Check whether we're closed.
const bool closed = (client_thread == nullptr || parent->IsClientClosed()); const bool closed = (client_thread == nullptr || parent->IsClientClosed());
@ -261,8 +176,8 @@ Result KServerSession::SendReply() {
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); auto* src_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
auto* dst_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress()); auto* dst_msg_buffer = memory.GetPointer(client_message);
std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize); std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
} else { } else {
result = ResultSessionClosed; result = ResultSessionClosed;
} }
@ -278,11 +193,30 @@ Result KServerSession::SendReply() {
// If there's a client thread, update it. // If there's a client thread, update it.
if (client_thread != nullptr) { if (client_thread != nullptr) {
// End the client thread's wait. if (event != nullptr) {
KScopedSchedulerLock sl{kernel}; // // Get the client process/page table.
// KProcess *client_process = client_thread->GetOwnerProcess();
// KPageTable *client_page_table = &client_process->PageTable();
if (!client_thread->IsTerminationRequested()) { // // If we need to, reply with an async error.
client_thread->EndWait(client_result); // if (R_FAILED(client_result)) {
// ReplyAsyncError(client_process, client_message, client_buffer_size,
// client_result);
// }
// // Unlock the client buffer.
// // NOTE: Nintendo does not check the result of this.
// client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
// Signal the event.
event->Signal();
} else {
// End the client thread's wait.
KScopedSchedulerLock sl{kernel};
if (!client_thread->IsTerminationRequested()) {
client_thread->EndWait(client_result);
}
} }
} }
@ -291,10 +225,10 @@ Result KServerSession::SendReply() {
Result KServerSession::ReceiveRequest() { Result KServerSession::ReceiveRequest() {
// Lock the session. // Lock the session.
KScopedLightLock lk(m_lock); KScopedLightLock lk{m_lock};
// Get the request and client thread. // Get the request and client thread.
// KSessionRequest *request; KSessionRequest* request;
KThread* client_thread; KThread* client_thread;
{ {
@ -304,35 +238,41 @@ Result KServerSession::ReceiveRequest() {
R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed); R_UNLESS(!parent->IsClientClosed(), ResultSessionClosed);
// Ensure we aren't already servicing a request. // Ensure we aren't already servicing a request.
R_UNLESS(m_current_thread_request == nullptr, ResultNotFound); R_UNLESS(m_current_request == nullptr, ResultNotFound);
// Ensure we have a request to service. // Ensure we have a request to service.
R_UNLESS(!m_thread_request_list.empty(), ResultNotFound); R_UNLESS(!m_request_list.empty(), ResultNotFound);
// Pop the first request from the list. // Pop the first request from the list.
client_thread = m_thread_request_list.front(); request = &m_request_list.front();
m_thread_request_list.pop_front(); m_request_list.pop_front();
// Get the thread for the request. // Get the thread for the request.
client_thread = request->GetThread();
R_UNLESS(client_thread != nullptr, ResultSessionClosed); R_UNLESS(client_thread != nullptr, ResultSessionClosed);
// Open the client thread. // Open the client thread.
client_thread->Open(); client_thread->Open();
} }
// SCOPE_EXIT({ client_thread->Close(); }); SCOPE_EXIT({ client_thread->Close(); });
// Set the request as our current. // Set the request as our current.
m_current_thread_request = client_thread; m_current_request = request;
// Get the client address.
uintptr_t client_message = request->GetAddress();
size_t client_buffer_size = request->GetSize();
// bool recv_list_broken = false;
// Receive the message. // Receive the message.
Core::Memory::Memory& memory{kernel.System().Memory()}; Core::Memory::Memory& memory{kernel.System().Memory()};
KThread* server_thread{GetCurrentThreadPointer(kernel)}; KThread* server_thread{GetCurrentThreadPointer(kernel)};
UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess());
auto* src_msg_buffer = memory.GetPointer(client_thread->GetTLSAddress()); auto* src_msg_buffer = memory.GetPointer(client_message);
auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress()); auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTLSAddress());
std::memcpy(dst_msg_buffer, src_msg_buffer, MessageBufferSize); std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size);
// We succeeded. // We succeeded.
return ResultSuccess; return ResultSuccess;
@ -344,35 +284,34 @@ void KServerSession::CleanupRequests() {
// Clean up any pending requests. // Clean up any pending requests.
while (true) { while (true) {
// Get the next request. // Get the next request.
// KSessionRequest *request = nullptr; KSessionRequest* request = nullptr;
KThread* client_thread = nullptr;
{ {
KScopedSchedulerLock sl{kernel}; KScopedSchedulerLock sl{kernel};
if (m_current_thread_request) { if (m_current_request) {
// Choose the current request if we have one. // Choose the current request if we have one.
client_thread = m_current_thread_request; request = m_current_request;
m_current_thread_request = nullptr; m_current_request = nullptr;
} else if (!m_thread_request_list.empty()) { } else if (!m_request_list.empty()) {
// Pop the request from the front of the list. // Pop the request from the front of the list.
client_thread = m_thread_request_list.front(); request = &m_request_list.front();
m_thread_request_list.pop_front(); m_request_list.pop_front();
} }
} }
// If there's no request, we're done. // If there's no request, we're done.
if (client_thread == nullptr) { if (request == nullptr) {
break; break;
} }
// Close a reference to the request once it's cleaned up. // Close a reference to the request once it's cleaned up.
SCOPE_EXIT({ client_thread->Close(); }); SCOPE_EXIT({ request->Close(); });
// Extract relevant information from the request. // Extract relevant information from the request.
// const uintptr_t client_message = request->GetAddress(); // const uintptr_t client_message = request->GetAddress();
// const size_t client_buffer_size = request->GetSize(); // const size_t client_buffer_size = request->GetSize();
// KThread *client_thread = request->GetThread(); KThread* client_thread = request->GetThread();
// KEvent *event = request->GetEvent(); KEvent* event = request->GetEvent();
// KProcess *server_process = request->GetServerProcess(); // KProcess *server_process = request->GetServerProcess();
// KProcess *client_process = (client_thread != nullptr) ? // KProcess *client_process = (client_thread != nullptr) ?
@ -385,11 +324,24 @@ void KServerSession::CleanupRequests() {
// If there's a client thread, update it. // If there's a client thread, update it.
if (client_thread != nullptr) { if (client_thread != nullptr) {
// End the client thread's wait. if (event != nullptr) {
KScopedSchedulerLock sl{kernel}; // // We need to reply async.
// ReplyAsyncError(client_process, client_message, client_buffer_size,
// (R_SUCCEEDED(result) ? ResultSessionClosed : result));
if (!client_thread->IsTerminationRequested()) { // // Unlock the client buffer.
client_thread->EndWait(ResultSessionClosed); // NOTE: Nintendo does not check the result of this.
// client_page_table->UnlockForIpcUserBuffer(client_message, client_buffer_size);
// Signal the event.
event->Signal();
} else {
// End the client thread's wait.
KScopedSchedulerLock sl{kernel};
if (!client_thread->IsTerminationRequested()) {
client_thread->EndWait(ResultSessionClosed);
}
} }
} }
} }

@ -12,6 +12,7 @@
#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_light_lock.h" #include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_session_request.h"
#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -57,44 +58,15 @@ public:
} }
bool IsSignaled() const override; bool IsSignaled() const override;
void OnClientClosed(); void OnClientClosed();
void ClientConnected(SessionRequestHandlerPtr handler) {
if (manager) {
manager->SetSessionHandler(std::move(handler));
}
}
void ClientDisconnected() {
manager = nullptr;
}
/// Adds a new domain request handler to the collection of request handlers within
/// this ServerSession instance.
void AppendDomainHandler(SessionRequestHandlerPtr handler);
/// Retrieves the total number of domain request handlers that have been
/// appended to this ServerSession instance.
std::size_t NumDomainRequestHandlers() const;
/// Returns true if the session has been converted to a domain, otherwise False
bool IsDomain() const {
return manager && manager->IsDomain();
}
/// Converts the session to a domain at the end of the current command
void ConvertToDomain() {
convert_to_domain = true;
}
/// Gets the session request manager, which forwards requests to the underlying service /// Gets the session request manager, which forwards requests to the underlying service
std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() { std::shared_ptr<SessionRequestManager>& GetSessionRequestManager() {
return manager; return manager;
} }
/// TODO: flesh these out to match the real kernel /// TODO: flesh these out to match the real kernel
Result OnRequest(); Result OnRequest(KSessionRequest* request);
Result SendReply(); Result SendReply();
Result ReceiveRequest(); Result ReceiveRequest();
@ -108,10 +80,6 @@ private:
/// Completes a sync request from the emulated application. /// Completes a sync request from the emulated application.
Result CompleteSyncRequest(HLERequestContext& context); Result CompleteSyncRequest(HLERequestContext& context);
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
/// object handle.
Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
/// This session's HLE request handlers; if nullptr, this is not an HLE server /// This session's HLE request handlers; if nullptr, this is not an HLE server
std::shared_ptr<SessionRequestManager> manager; std::shared_ptr<SessionRequestManager> manager;
@ -122,9 +90,8 @@ private:
KSession* parent{}; KSession* parent{};
/// List of threads which are pending a reply. /// List of threads which are pending a reply.
/// FIXME: KSessionRequest boost::intrusive::list<KSessionRequest> m_request_list;
std::list<KThread*> m_thread_request_list; KSessionRequest* m_current_request;
KThread* m_current_thread_request{};
KLightLock m_lock; KLightLock m_lock;
}; };

@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_session_request.h"
namespace Kernel {
Result KSessionRequest::SessionMappings::PushMap(VAddr client, VAddr server, size_t size,
KMemoryState state, size_t index) {
// At most 15 buffers of each type (4-bit descriptor counts).
ASSERT(index < ((1ul << 4) - 1) * 3);
// Get the mapping.
Mapping* mapping;
if (index < NumStaticMappings) {
mapping = &m_static_mappings[index];
} else {
// Allocate a page for the extra mappings.
if (m_mappings == nullptr) {
KPageBuffer* page_buffer = KPageBuffer::Allocate(kernel);
R_UNLESS(page_buffer != nullptr, ResultOutOfMemory);
m_mappings = reinterpret_cast<Mapping*>(page_buffer);
}
mapping = &m_mappings[index - NumStaticMappings];
}
// Set the mapping.
mapping->Set(client, server, size, state);
return ResultSuccess;
}
Result KSessionRequest::SessionMappings::PushSend(VAddr client, VAddr server, size_t size,
KMemoryState state) {
ASSERT(m_num_recv == 0);
ASSERT(m_num_exch == 0);
return this->PushMap(client, server, size, state, m_num_send++);
}
Result KSessionRequest::SessionMappings::PushReceive(VAddr client, VAddr server, size_t size,
KMemoryState state) {
ASSERT(m_num_exch == 0);
return this->PushMap(client, server, size, state, m_num_send + m_num_recv++);
}
Result KSessionRequest::SessionMappings::PushExchange(VAddr client, VAddr server, size_t size,
KMemoryState state) {
return this->PushMap(client, server, size, state, m_num_send + m_num_recv + m_num_exch++);
}
void KSessionRequest::SessionMappings::Finalize() {
if (m_mappings) {
KPageBuffer::Free(kernel, reinterpret_cast<KPageBuffer*>(m_mappings));
m_mappings = nullptr;
}
}
} // namespace Kernel

@ -0,0 +1,307 @@
// SPDX-FileCopyrightText: Copyright 2022 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_event.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/slab_helpers.h"
namespace Kernel {
class KSessionRequest final : public KSlabAllocated<KSessionRequest>,
public KAutoObject,
public boost::intrusive::list_base_hook<> {
KERNEL_AUTOOBJECT_TRAITS(KSessionRequest, KAutoObject);
public:
class SessionMappings {
private:
static constexpr size_t NumStaticMappings = 8;
class Mapping {
public:
constexpr void Set(VAddr c, VAddr s, size_t sz, KMemoryState st) {
m_client_address = c;
m_server_address = s;
m_size = sz;
m_state = st;
}
constexpr VAddr GetClientAddress() const {
return m_client_address;
}
constexpr VAddr GetServerAddress() const {
return m_server_address;
}
constexpr size_t GetSize() const {
return m_size;
}
constexpr KMemoryState GetMemoryState() const {
return m_state;
}
private:
VAddr m_client_address;
VAddr m_server_address;
size_t m_size;
KMemoryState m_state;
};
public:
explicit SessionMappings(KernelCore& kernel_)
: kernel(kernel_), m_mappings(nullptr), m_num_send(), m_num_recv(), m_num_exch() {}
void Initialize() {}
void Finalize();
size_t GetSendCount() const {
return m_num_send;
}
size_t GetReceiveCount() const {
return m_num_recv;
}
size_t GetExchangeCount() const {
return m_num_exch;
}
Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state);
Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state);
Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state);
VAddr GetSendClientAddress(size_t i) const {
return GetSendMapping(i).GetClientAddress();
}
VAddr GetSendServerAddress(size_t i) const {
return GetSendMapping(i).GetServerAddress();
}
size_t GetSendSize(size_t i) const {
return GetSendMapping(i).GetSize();
}
KMemoryState GetSendMemoryState(size_t i) const {
return GetSendMapping(i).GetMemoryState();
}
VAddr GetReceiveClientAddress(size_t i) const {
return GetReceiveMapping(i).GetClientAddress();
}
VAddr GetReceiveServerAddress(size_t i) const {
return GetReceiveMapping(i).GetServerAddress();
}
size_t GetReceiveSize(size_t i) const {
return GetReceiveMapping(i).GetSize();
}
KMemoryState GetReceiveMemoryState(size_t i) const {
return GetReceiveMapping(i).GetMemoryState();
}
VAddr GetExchangeClientAddress(size_t i) const {
return GetExchangeMapping(i).GetClientAddress();
}
VAddr GetExchangeServerAddress(size_t i) const {
return GetExchangeMapping(i).GetServerAddress();
}
size_t GetExchangeSize(size_t i) const {
return GetExchangeMapping(i).GetSize();
}
KMemoryState GetExchangeMemoryState(size_t i) const {
return GetExchangeMapping(i).GetMemoryState();
}
private:
Result PushMap(VAddr client, VAddr server, size_t size, KMemoryState state, size_t index);
const Mapping& GetSendMapping(size_t i) const {
ASSERT(i < m_num_send);
const size_t index = i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
const Mapping& GetReceiveMapping(size_t i) const {
ASSERT(i < m_num_recv);
const size_t index = m_num_send + i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
const Mapping& GetExchangeMapping(size_t i) const {
ASSERT(i < m_num_exch);
const size_t index = m_num_send + m_num_recv + i;
if (index < NumStaticMappings) {
return m_static_mappings[index];
} else {
return m_mappings[index - NumStaticMappings];
}
}
private:
KernelCore& kernel;
Mapping m_static_mappings[NumStaticMappings];
Mapping* m_mappings;
u8 m_num_send;
u8 m_num_recv;
u8 m_num_exch;
};
public:
explicit KSessionRequest(KernelCore& kernel_)
: KAutoObject(kernel_), m_mappings(kernel_), m_thread(nullptr), m_server(nullptr),
m_event(nullptr) {}
static KSessionRequest* Create(KernelCore& kernel) {
KSessionRequest* req = KSessionRequest::Allocate(kernel);
if (req != nullptr) [[likely]] {
KAutoObject::Create(req);
}
return req;
}
void Destroy() override {
this->Finalize();
KSessionRequest::Free(kernel, this);
}
void Initialize(KEvent* event, uintptr_t address, size_t size) {
m_mappings.Initialize();
m_thread = GetCurrentThreadPointer(kernel);
m_event = event;
m_address = address;
m_size = size;
m_thread->Open();
if (m_event != nullptr) {
m_event->Open();
}
}
static void PostDestroy(uintptr_t arg) {}
KThread* GetThread() const {
return m_thread;
}
KEvent* GetEvent() const {
return m_event;
}
uintptr_t GetAddress() const {
return m_address;
}
size_t GetSize() const {
return m_size;
}
KProcess* GetServerProcess() const {
return m_server;
}
void SetServerProcess(KProcess* process) {
m_server = process;
m_server->Open();
}
void ClearThread() {
m_thread = nullptr;
}
void ClearEvent() {
m_event = nullptr;
}
size_t GetSendCount() const {
return m_mappings.GetSendCount();
}
size_t GetReceiveCount() const {
return m_mappings.GetReceiveCount();
}
size_t GetExchangeCount() const {
return m_mappings.GetExchangeCount();
}
Result PushSend(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushSend(client, server, size, state);
}
Result PushReceive(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushReceive(client, server, size, state);
}
Result PushExchange(VAddr client, VAddr server, size_t size, KMemoryState state) {
return m_mappings.PushExchange(client, server, size, state);
}
VAddr GetSendClientAddress(size_t i) const {
return m_mappings.GetSendClientAddress(i);
}
VAddr GetSendServerAddress(size_t i) const {
return m_mappings.GetSendServerAddress(i);
}
size_t GetSendSize(size_t i) const {
return m_mappings.GetSendSize(i);
}
KMemoryState GetSendMemoryState(size_t i) const {
return m_mappings.GetSendMemoryState(i);
}
VAddr GetReceiveClientAddress(size_t i) const {
return m_mappings.GetReceiveClientAddress(i);
}
VAddr GetReceiveServerAddress(size_t i) const {
return m_mappings.GetReceiveServerAddress(i);
}
size_t GetReceiveSize(size_t i) const {
return m_mappings.GetReceiveSize(i);
}
KMemoryState GetReceiveMemoryState(size_t i) const {
return m_mappings.GetReceiveMemoryState(i);
}
VAddr GetExchangeClientAddress(size_t i) const {
return m_mappings.GetExchangeClientAddress(i);
}
VAddr GetExchangeServerAddress(size_t i) const {
return m_mappings.GetExchangeServerAddress(i);
}
size_t GetExchangeSize(size_t i) const {
return m_mappings.GetExchangeSize(i);
}
KMemoryState GetExchangeMemoryState(size_t i) const {
return m_mappings.GetExchangeMemoryState(i);
}
private:
// NOTE: This is public and virtual in Nintendo's kernel.
void Finalize() {
m_mappings.Finalize();
if (m_thread) {
m_thread->Close();
}
if (m_event) {
m_event->Close();
}
if (m_server) {
m_server->Close();
}
}
private:
SessionMappings m_mappings;
KThread* m_thread;
KProcess* m_server;
KEvent* m_event;
uintptr_t m_address;
size_t m_size;
};
} // namespace Kernel

@ -15,7 +15,8 @@ class KSharedMemoryInfo final : public KSlabAllocated<KSharedMemoryInfo>,
public boost::intrusive::list_base_hook<> { public boost::intrusive::list_base_hook<> {
public: public:
explicit KSharedMemoryInfo() = default; explicit KSharedMemoryInfo(KernelCore&) {}
KSharedMemoryInfo() = default;
constexpr void Initialize(KSharedMemory* shmem) { constexpr void Initialize(KSharedMemory* shmem) {
shared_memory = shmem; shared_memory = shmem;

@ -26,7 +26,7 @@ public:
static_assert(RegionsPerPage > 0); static_assert(RegionsPerPage > 0);
public: public:
constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) { constexpr explicit KThreadLocalPage(KernelCore&, VAddr addr = {}) : m_virt_addr(addr) {
m_is_region_free.fill(true); m_is_region_free.fill(true);
} }

@ -47,6 +47,7 @@ class KResourceLimit;
class KScheduler; class KScheduler;
class KServerSession; class KServerSession;
class KSession; class KSession;
class KSessionRequest;
class KSharedMemory; class KSharedMemory;
class KSharedMemoryInfo; class KSharedMemoryInfo;
class KThread; class KThread;
@ -360,6 +361,8 @@ public:
return slab_heap_container->page_buffer; return slab_heap_container->page_buffer;
} else if constexpr (std::is_same_v<T, KThreadLocalPage>) { } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
return slab_heap_container->thread_local_page; return slab_heap_container->thread_local_page;
} else if constexpr (std::is_same_v<T, KSessionRequest>) {
return slab_heap_container->session_request;
} }
} }
@ -422,6 +425,7 @@ private:
KSlabHeap<KCodeMemory> code_memory; KSlabHeap<KCodeMemory> code_memory;
KSlabHeap<KPageBuffer> page_buffer; KSlabHeap<KPageBuffer> page_buffer;
KSlabHeap<KThreadLocalPage> thread_local_page; KSlabHeap<KThreadLocalPage> thread_local_page;
KSlabHeap<KSessionRequest> session_request;
}; };
std::unique_ptr<SlabHeapContainer> slab_heap_container; std::unique_ptr<SlabHeapContainer> slab_heap_container;

@ -24,7 +24,7 @@ public:
} }
static Derived* Allocate(KernelCore& kernel) { static Derived* Allocate(KernelCore& kernel) {
return kernel.SlabHeap<Derived>().Allocate(); return kernel.SlabHeap<Derived>().Allocate(kernel);
} }
static void Free(KernelCore& kernel, Derived* obj) { static void Free(KernelCore& kernel, Derived* obj) {

@ -15,9 +15,10 @@
namespace Service::SM { namespace Service::SM {
void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) { void Controller::ConvertCurrentObjectToDomain(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(!ctx.Session()->IsDomain(), "Session is already a domain"); ASSERT_MSG(!ctx.Session()->GetSessionRequestManager()->IsDomain(),
"Session is already a domain");
LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId()); LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetId());
ctx.Session()->ConvertToDomain(); ctx.Session()->GetSessionRequestManager()->ConvertToDomainOnRequestEnd();
IPC::ResponseBuilder rb{ctx, 3}; IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);