From 01a2d978ebc24aa517048f941f53213db9de722e Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 25 Jan 2024 00:50:03 -0500 Subject: [PATCH] service: add template serializer for method calls --- src/core/CMakeLists.txt | 2 + src/core/hle/service/cmif_serialization.h | 337 ++++++++++++++++++++++ src/core/hle/service/cmif_types.h | 234 +++++++++++++++ src/core/hle/service/hle_ipc.cpp | 16 + src/core/hle/service/hle_ipc.h | 2 + src/core/hle/service/jit/jit.cpp | 275 +++++------------- src/core/hle/service/ro/ro.cpp | 192 +++--------- src/core/hle/service/service.h | 16 + 8 files changed, 725 insertions(+), 349 deletions(-) create mode 100644 src/core/hle/service/cmif_serialization.h create mode 100644 src/core/hle/service/cmif_types.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4ff2c1bb74..4a1e866a00 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -472,6 +472,8 @@ add_library(core STATIC hle/service/caps/caps_types.h hle/service/caps/caps_u.cpp hle/service/caps/caps_u.h + hle/service/cmif_serialization.h + hle/service/cmif_types.h hle/service/erpt/erpt.cpp hle/service/erpt/erpt.h hle/service/es/es.cpp diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h new file mode 100644 index 0000000000..8e8cf2507e --- /dev/null +++ b/src/core/hle/service/cmif_serialization.h @@ -0,0 +1,337 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/div_ceil.h" + +#include "core/hle/service/cmif_types.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/service.h" + +namespace Service { + +// clang-format off +struct RequestLayout { + u32 copy_handle_count; + u32 move_handle_count; + u32 cmif_raw_data_size; + u32 domain_interface_count; +}; + +template +constexpr u32 GetArgumentRawDataSize() { + if constexpr (ArgIndex >= std::tuple_size_v) { + return static_cast(DataOffset); + } else { + using ArgType = std::tuple_element_t; + + if constexpr (ArgumentTraits::Type == Type1 || ArgumentTraits::Type == Type2) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + return GetArgumentRawDataSize(); + } else { + return GetArgumentRawDataSize(); + } + } +} + +template +constexpr u32 GetArgumentTypeCount() { + if constexpr (ArgIndex >= std::tuple_size_v) { + return static_cast(ArgCount); + } else { + using ArgType = std::tuple_element_t; + + if constexpr (ArgumentTraits::Type == DataType) { + return GetArgumentTypeCount(); + } else { + return GetArgumentTypeCount(); + } + } +} + +template +constexpr RequestLayout GetNonDomainReplyInLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount(), + .move_handle_count = 0, + .cmif_raw_data_size = GetArgumentRawDataSize(), + .domain_interface_count = 0, + }; +} + +template +constexpr RequestLayout GetDomainReplyInLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount(), + .move_handle_count = 0, + .cmif_raw_data_size = GetArgumentRawDataSize(), + .domain_interface_count = GetArgumentTypeCount(), + }; +} + +template +constexpr RequestLayout GetNonDomainReplyOutLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount(), + .move_handle_count = GetArgumentTypeCount() + GetArgumentTypeCount(), + .cmif_raw_data_size = GetArgumentRawDataSize(), + .domain_interface_count = 0, + }; +} + +template +constexpr RequestLayout GetDomainReplyOutLayout() { + return RequestLayout{ + .copy_handle_count = GetArgumentTypeCount(), + .move_handle_count = GetArgumentTypeCount(), + .cmif_raw_data_size = GetArgumentRawDataSize(), + .domain_interface_count = GetArgumentTypeCount(), + }; +} + +template +constexpr RequestLayout GetReplyInLayout() { + return Domain ? GetDomainReplyInLayout() : GetNonDomainReplyInLayout(); +} + +template +constexpr RequestLayout GetReplyOutLayout() { + return Domain ? GetDomainReplyOutLayout() : GetNonDomainReplyOutLayout(); +} + +using OutTemporaryBuffers = std::array, 3>; + +template +void ReadInArgument(CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { + if constexpr (ArgIndex >= std::tuple_size_v) { + return; + } else { + using ArgType = std::tuple_element_t; + + if constexpr (ArgumentTraits::Type == ArgumentType::InData || ArgumentTraits::Type == ArgumentType::InProcessId) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Input argument is not ordered by alignment"); + static_assert(!RawDataFinished, "All input interface arguments must appear after raw data"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + if constexpr (ArgumentTraits::Type == ArgumentType::InProcessId) { + // TODO: abort parsing if PID is not provided? + // TODO: validate against raw data value? + std::get(args).pid = ctx.GetPID(); + } else { + std::memcpy(&std::get(args), raw_data + ArgOffset, ArgSize); + } + + return ReadInArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::InInterface) { + constexpr size_t ArgAlign = alignof(u32); + constexpr size_t ArgSize = sizeof(u32); + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + static_assert(Domain); + ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0); + + u32 value{}; + std::memcpy(&value, raw_data + ArgOffset, ArgSize); + std::get(args) = ctx.GetDomainHandler(value - 1); + + return ReadInArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::InCopyHandle) { + std::get(args) = std::move(ctx.GetObjectFromHandle(ctx.GetCopyHandle(HandleIndex))); + + return ReadInArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::InLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + // Clear the existing data. + std::memset(&std::get(args), 0, BufferSize); + + std::span buffer{}; + + ASSERT(ctx.CanReadBuffer(InBufferIndex)); + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + buffer = ctx.ReadBuffer(InBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + buffer = ctx.ReadBufferA(InBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + buffer = ctx.ReadBufferX(InBufferIndex); + } + + std::memcpy(&std::get(args), buffer.data(), std::min(BufferSize, buffer.size())); + + return ReadInArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::InBuffer) { + using ElementType = typename ArgType::Type; + + std::span buffer{}; + + if (ctx.CanReadBuffer(InBufferIndex)) { + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + buffer = ctx.ReadBuffer(InBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + buffer = ctx.ReadBufferA(InBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + buffer = ctx.ReadBufferX(InBufferIndex); + } + } + + ElementType* ptr = (ElementType*) buffer.data(); + size_t size = buffer.size() / sizeof(ElementType); + + std::get(args) = std::span(ptr, size); + + return ReadInArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::OutLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + // Clear the existing data. + std::memset(&std::get(args), 0, BufferSize); + + return ReadInArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::OutBuffer) { + using ElementType = typename ArgType::Type; + + // Set up scratch buffer. + auto& buffer = temp[OutBufferIndex]; + if (ctx.CanWriteBuffer(OutBufferIndex)) { + buffer.resize_destructive(ctx.GetWriteBufferSize(OutBufferIndex)); + } else { + buffer.resize_destructive(0); + } + + ElementType* ptr = (ElementType*) buffer.data(); + size_t size = buffer.size() / sizeof(ElementType); + + std::get(args) = std::span(ptr, size); + + return ReadInArgument(args, raw_data, ctx, temp); + } else { + return ReadInArgument(args, raw_data, ctx, temp); + } + } +} + +template +void WriteOutArgument(CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { + if constexpr (ArgIndex >= std::tuple_size_v) { + return; + } else { + using ArgType = std::tuple_element_t; + + if constexpr (ArgumentTraits::Type == ArgumentType::OutData) { + constexpr size_t ArgAlign = alignof(ArgType); + constexpr size_t ArgSize = sizeof(ArgType); + + static_assert(PrevAlign <= ArgAlign, "Output argument is not ordered by alignment"); + static_assert(!RawDataFinished, "All output interface arguments must appear after raw data"); + + constexpr size_t ArgOffset = Common::AlignUp(DataOffset, ArgAlign); + constexpr size_t ArgEnd = ArgOffset + ArgSize; + + std::memcpy(raw_data + ArgOffset, &std::get(args), ArgSize); + + return WriteOutArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::OutInterface) { + if constexpr (Domain) { + ctx.AddDomainObject(std::get(args)); + } else { + ctx.AddMoveInterface(std::get(args)); + } + + return WriteOutArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::OutCopyHandle) { + ctx.AddCopyObject(std::get(args).GetPointerUnsafe()); + + return WriteOutArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::OutMoveHandle) { + ctx.AddMoveObject(std::get(args).GetPointerUnsafe()); + + return WriteOutArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::OutLargeData) { + constexpr size_t BufferSize = sizeof(ArgType); + + ASSERT(ctx.CanWriteBuffer(OutBufferIndex)); + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + ctx.WriteBuffer(std::get(args), OutBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + ctx.WriteBufferB(&std::get(args), BufferSize, OutBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + ctx.WriteBufferC(&std::get(args), BufferSize, OutBufferIndex); + } + + return WriteOutArgument(args, raw_data, ctx, temp); + } else if constexpr (ArgumentTraits::Type == ArgumentType::OutBuffer) { + auto& buffer = temp[OutBufferIndex]; + const size_t size = buffer.size(); + + if (ctx.CanWriteBuffer(OutBufferIndex)) { + if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { + ctx.WriteBuffer(buffer.data(), size, OutBufferIndex); + } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { + ctx.WriteBufferB(buffer.data(), size, OutBufferIndex); + } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { + ctx.WriteBufferC(buffer.data(), size, OutBufferIndex); + } + } + + return WriteOutArgument( args, raw_data, ctx, temp); + } else { + return WriteOutArgument(args, raw_data, ctx, temp); + } + } +} + +template +void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) { + // Verify domain state. + if constexpr (Domain) { + ASSERT_MSG(ctx.GetManager()->IsDomain(), "Domain reply used on non-domain session"); + } else { + ASSERT_MSG(!ctx.GetManager()->IsDomain(), "Non-domain reply used on domain session"); + } + + using MethodArguments = std::tuple...>; + + OutTemporaryBuffers buffers{}; + auto call_arguments = std::tuple::Type...>(); + + // Read inputs. + const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2; + ReadInArgument(call_arguments, reinterpret_cast(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers); + + // Call. + const auto Callable = [&](CallArgs&... args) { + return (t.*f)(args...); + }; + const Result res = std::apply(Callable, call_arguments); + + // Write result. + constexpr RequestLayout layout = GetReplyOutLayout(); + IPC::ResponseBuilder rb{ctx, 2 + Common::DivCeil(layout.cmif_raw_data_size, sizeof(u32)), layout.copy_handle_count, layout.move_handle_count + layout.domain_interface_count}; + rb.Push(res); + + // Write out arguments. + WriteOutArgument(call_arguments, reinterpret_cast(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers); +} +// clang-format on + +template +template +inline void ServiceFramework::CmifReplyWrap(HLERequestContext& ctx) { + return CmifReplyWrapImpl(ctx, *static_cast(this), F); +} + +} // namespace Service diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h new file mode 100644 index 0000000000..b80028c19d --- /dev/null +++ b/src/core/hle/service/cmif_types.h @@ -0,0 +1,234 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/service/hle_ipc.h" + +namespace Service { + +// clang-format off +template +class Out { +public: + /* implicit */ Out(T& t) : raw(&t) {} + ~Out() = default; + + T* Get() const { + return raw; + } + + T& operator*() { + return *raw; + } + +private: + T* raw; +}; + +template +using SharedPointer = std::shared_ptr; + +struct ClientProcessId { + explicit operator bool() const { + return pid != 0; + } + + const u64& operator*() const { + return pid; + } + + u64 pid; +}; + +using ClientAppletResourceUserId = ClientProcessId; + +template +class InCopyHandle : public Kernel::KScopedAutoObject { +public: + using Type = T; + + template + /* implicit */ InCopyHandle(Args&&... args) : Kernel::KScopedAutoObject(std::forward(args)...) {} + ~InCopyHandle() = default; + + InCopyHandle& operator=(InCopyHandle&& rhs) { + Kernel::KScopedAutoObject::operator=(std::move(rhs)); + return *this; + } +}; + +template +class OutCopyHandle : public Kernel::KScopedAutoObject { +public: + using Type = T; + + template + /* implicit */ OutCopyHandle(Args&&... args) : Kernel::KScopedAutoObject(std::forward(args)...) {} + ~OutCopyHandle() = default; + + OutCopyHandle& operator=(OutCopyHandle&& rhs) { + Kernel::KScopedAutoObject::operator=(std::move(rhs)); + return *this; + } +}; + +template +class OutMoveHandle : public Kernel::KScopedAutoObject { +public: + using Type = T; + + template + /* implicit */ OutMoveHandle(Args&&... args) : Kernel::KScopedAutoObject(std::forward(args)...) {} + ~OutMoveHandle() = default; + + OutMoveHandle& operator=(OutMoveHandle&& rhs) { + Kernel::KScopedAutoObject::operator=(std::move(rhs)); + return *this; + } +}; + +enum BufferAttr : int { + BufferAttr_In = (1U << 0), + BufferAttr_Out = (1U << 1), + BufferAttr_HipcMapAlias = (1U << 2), + BufferAttr_HipcPointer = (1U << 3), + BufferAttr_FixedSize = (1U << 4), + BufferAttr_HipcAutoSelect = (1U << 5), + BufferAttr_HipcMapTransferAllowsNonSecure = (1U << 6), + BufferAttr_HipcMapTransferAllowsNonDevice = (1U << 7), +}; + +template +struct Buffer : public std::span { + static_assert(std::is_trivial_v, "Buffer type must be trivial"); + static_assert((A & BufferAttr_FixedSize) == 0, "Buffer attr must not contain FixedSize"); + static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "Buffer attr must be In or Out"); + static constexpr BufferAttr Attr = static_cast(A); + using Type = T; + + Buffer& operator=(const std::span& rhs) { + std::span::operator=(rhs); + return *this; + } + + T& operator*() const { + return *this->data(); + } + + explicit operator bool() const { + return this->size() > 0; + } +}; + +template +using InBuffer = Buffer; + +template +using InArray = Buffer; + +template +using OutBuffer = Buffer; + +template +using OutArray = Buffer; + +template +struct LargeData : public T { + static_assert(std::is_trivial_v, "LargeData type must be trivial"); + static_assert((A & BufferAttr_FixedSize) != 0, "LargeData attr must contain FixedSize"); + static_assert(((A & BufferAttr_In) == 0) ^ ((A & BufferAttr_Out) == 0), "LargeData attr must be In or Out"); + static constexpr BufferAttr Attr = static_cast(A); + using Type = T; +}; + +template +using InLargeData = LargeData; + +template +using OutLargeData = LargeData; + +template +struct RemoveOut { + using Type = std::remove_reference_t; +}; + +template +struct RemoveOut> { + using Type = T; +}; + +enum class ArgumentType { + InProcessId, + InData, + InInterface, + InCopyHandle, + OutData, + OutInterface, + OutCopyHandle, + OutMoveHandle, + InBuffer, + InLargeData, + OutBuffer, + OutLargeData, +}; + +template +struct ArgumentTraits; + +template <> +struct ArgumentTraits { + static constexpr ArgumentType Type = ArgumentType::InProcessId; +}; + +template +struct ArgumentTraits> { + static constexpr ArgumentType Type = ArgumentType::InInterface; +}; + +template +struct ArgumentTraits> { + static constexpr ArgumentType Type = ArgumentType::InCopyHandle; +}; + +template +struct ArgumentTraits>> { + static constexpr ArgumentType Type = ArgumentType::OutInterface; +}; + +template +struct ArgumentTraits> { + static constexpr ArgumentType Type = ArgumentType::OutData; +}; + +template +struct ArgumentTraits> { + static constexpr ArgumentType Type = ArgumentType::OutCopyHandle; +}; + +template +struct ArgumentTraits> { + static constexpr ArgumentType Type = ArgumentType::OutMoveHandle; +}; + +template +struct ArgumentTraits> { + static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer; +}; + +template +struct ArgumentTraits> { + static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData; +}; + +template +struct ArgumentTraits { + static constexpr ArgumentType Type = ArgumentType::InData; +}; +// clang-format on + +} // namespace Service diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index e491dd2604..50e1ed7565 100644 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -501,6 +501,22 @@ bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const { } } +void HLERequestContext::AddMoveInterface(SessionRequestHandlerPtr s) { + ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve( + Kernel::LimitableResource::SessionCountMax, 1)); + + auto* session = Kernel::KSession::Create(kernel); + session->Initialize(nullptr, 0); + Kernel::KSession::Register(kernel, session); + + auto& server = manager.lock()->GetServerManager(); + auto next_manager = std::make_shared(kernel, server); + next_manager->SetSessionHandler(std::move(s)); + server.RegisterSession(&session->GetServerSession(), next_manager); + + AddMoveObject(&session->GetClientSession()); +} + std::string HLERequestContext::Description() const { if (!command_header) { return "No command header available"; diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index 8329d72653..c2e0e5e8c4 100644 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -339,6 +339,8 @@ public: outgoing_move_objects.emplace_back(object); } + void AddMoveInterface(SessionRequestHandlerPtr s); + void AddCopyObject(Kernel::KAutoObject* object) { outgoing_copy_objects.emplace_back(object); } diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 77aa6d7d13..d8fefff894 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -6,12 +6,12 @@ #include "core/core.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/result.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/jit/jit.h" #include "core/hle/service/jit/jit_code_memory.h" #include "core/hle/service/jit/jit_context.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" #include "core/memory.h" namespace Service::JIT { @@ -21,6 +21,9 @@ struct CodeRange { u64 size; }; +using Struct32 = std::array; +static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size"); + class IJitEnvironment final : public ServiceFramework { public: explicit IJitEnvironment(Core::System& system_, @@ -29,12 +32,13 @@ public: : ServiceFramework{system_, "IJitEnvironment"}, process{std::move(process_)}, user_rx{std::move(user_rx_)}, user_ro{std::move(user_ro_)}, context{system_.ApplicationMemory()} { + // clang-format off static const FunctionInfo functions[] = { - {0, &IJitEnvironment::GenerateCode, "GenerateCode"}, - {1, &IJitEnvironment::Control, "Control"}, - {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"}, - {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"}, + {0, C<&IJitEnvironment::GenerateCode>, "GenerateCode"}, + {1, C<&IJitEnvironment::Control>, "Control"}, + {1000, C<&IJitEnvironment::LoadPlugin>, "LoadPlugin"}, + {1001, C<&IJitEnvironment::GetCodeAddress>, "GetCodeAddress"}, }; // clang-format on @@ -50,28 +54,10 @@ public: configuration.sys_ro_memory = configuration.user_ro_memory; } - void GenerateCode(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - struct InputParameters { - u32 data_size; - u64 command; - std::array ranges; - Struct32 data; - }; - - struct OutputParameters { - s32 return_value; - std::array ranges; - }; - - IPC::RequestParser rp{ctx}; - const auto parameters{rp.PopRaw()}; - - // Optional input/output buffers - const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span()}; - std::vector output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); - + Result GenerateCode(Out out_return_value, Out out_range0, + Out out_range1, OutBuffer out_buffer, + u32 data_size, u64 command, CodeRange range0, CodeRange range1, + Struct32 data, InBuffer buffer) { // Function call prototype: // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg, // u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in, @@ -83,66 +69,36 @@ public: // other arguments are used to transfer state between the game and the plugin. const VAddr ret_ptr{context.AddHeap(0u)}; - const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])}; - const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])}; - const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))}; - const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))}; + const VAddr c0_in_ptr{context.AddHeap(range0)}; + const VAddr c1_in_ptr{context.AddHeap(range1)}; + const VAddr c0_out_ptr{context.AddHeap(ClearSize(range0))}; + const VAddr c1_out_ptr{context.AddHeap(ClearSize(range1))}; - const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; - const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; - const VAddr data_ptr{context.AddHeap(parameters.data)}; + const VAddr input_ptr{context.AddHeap(buffer.data(), buffer.size())}; + const VAddr output_ptr{context.AddHeap(out_buffer.data(), out_buffer.size())}; + const VAddr data_ptr{context.AddHeap(data)}; const VAddr configuration_ptr{context.AddHeap(configuration)}; // The callback does not directly return a value, it only writes to the output pointer context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr, - configuration_ptr, parameters.command, input_ptr, input_buffer.size(), - c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr, - output_buffer.size()); + configuration_ptr, command, input_ptr, buffer.size(), c0_in_ptr, + c1_in_ptr, data_ptr, data_size, output_ptr, out_buffer.size()); - const s32 return_value{context.GetHeap(ret_ptr)}; + *out_return_value = context.GetHeap(ret_ptr); + *out_range0 = context.GetHeap(c0_out_ptr); + *out_range1 = context.GetHeap(c1_out_ptr); + context.GetHeap(output_ptr, out_buffer.data(), out_buffer.size()); - if (return_value == 0) { - // The callback has written to the output executable code range, - // requiring an instruction cache invalidation - Core::InvalidateInstructionCacheRange(process.GetPointerUnsafe(), - configuration.user_rx_memory.offset, - configuration.user_rx_memory.size); - - // Write back to the IPC output buffer, if provided - if (ctx.CanWriteBuffer()) { - context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); - ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); - } - - const OutputParameters out{ - .return_value = return_value, - .ranges = - { - context.GetHeap(c0_out_ptr), - context.GetHeap(c1_out_ptr), - }, - }; - - IPC::ResponseBuilder rb{ctx, 8}; - rb.Push(ResultSuccess); - rb.PushRaw(out); - } else { + if (*out_return_value != 0) { LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); + R_THROW(ResultUnknown); } - }; - void Control(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - IPC::RequestParser rp{ctx}; - const auto command{rp.PopRaw()}; - - // Optional input/output buffers - const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span()}; - std::vector output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); + R_SUCCEED(); + } + Result Control(Out out_return_value, InBuffer in_data, + OutBuffer out_data, u64 command) { // Function call prototype: // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size, // u8* output_buf, size_t output_size); @@ -152,53 +108,30 @@ public: const VAddr ret_ptr{context.AddHeap(0u)}; const VAddr configuration_ptr{context.AddHeap(configuration)}; - const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())}; - const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())}; + const VAddr input_ptr{context.AddHeap(in_data.data(), in_data.size())}; + const VAddr output_ptr{context.AddHeap(out_data.data(), out_data.size())}; const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr, - command, input_ptr, input_buffer.size(), - output_ptr, output_buffer.size())}; + command, input_ptr, in_data.size(), output_ptr, + out_data.size())}; - const s32 return_value{context.GetHeap(ret_ptr)}; + *out_return_value = context.GetHeap(ret_ptr); + context.GetHeap(output_ptr, out_data.data(), out_data.size()); - if (wrapper_value == 0 && return_value == 0) { - // Write back to the IPC output buffer, if provided - if (ctx.CanWriteBuffer()) { - context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size()); - ctx.WriteBuffer(output_buffer.data(), output_buffer.size()); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(return_value); - } else { - LOG_WARNING(Service_JIT, "plugin Control callback failed"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); + if (wrapper_value == 0 && *out_return_value == 0) { + R_SUCCEED(); } + + LOG_WARNING(Service_JIT, "plugin Control callback failed"); + R_THROW(ResultUnknown); } - void LoadPlugin(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - IPC::RequestParser rp{ctx}; - const auto tmem_size{rp.PopRaw()}; - const auto tmem_handle{ctx.GetCopyHandle(0)}; - const auto nro_plugin{ctx.ReadBuffer(1)}; - - if (tmem_size == 0) { - LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto tmem{ctx.GetObjectFromHandle(tmem_handle)}; + Result LoadPlugin(u64 tmem_size, InCopyHandle& tmem, + InBuffer nrr, + InBuffer nro) { if (tmem.IsNull()) { - LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + LOG_ERROR(Service_JIT, "Invalid transfer memory handle!"); + R_THROW(ResultUnknown); } // Set up the configuration with the required TransferMemory address @@ -206,7 +139,7 @@ public: configuration.transfer_memory.size = tmem_size; // Gather up all the callbacks from the loaded plugin - auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)}; + auto symbols{Core::Symbols::GetSymbols(nro, true)}; const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }}; callbacks.rtld_fini = GetSymbol("_fini"); @@ -223,16 +156,12 @@ public: if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 || callbacks.OnPrepared == 0) { LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } - if (!context.LoadNRO(nro_plugin)) { + if (!context.LoadNRO(nro)) { LOG_ERROR(Service_JIT, "failed to load plugin"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } context.MapProcessMemory(configuration.sys_ro_memory.offset, @@ -252,9 +181,7 @@ public: const auto version{context.CallFunction(callbacks.GetVersion)}; if (version != 1) { LOG_ERROR(Service_JIT, "unknown plugin version {}", version); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } // Function prototype: @@ -280,22 +207,19 @@ public: const auto configuration_ptr{context.AddHeap(configuration)}; context.CallFunction(callbacks.OnPrepared, configuration_ptr); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } - void GetCodeAddress(HLERequestContext& ctx) { + Result GetCodeAddress(Out rx_offset, Out ro_offset) { LOG_DEBUG(Service_JIT, "called"); - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.Push(configuration.user_rx_memory.offset); - rb.Push(configuration.user_ro_memory.offset); + *rx_offset = configuration.user_rx_memory.offset; + *ro_offset = configuration.user_ro_memory.offset; + + R_SUCCEED(); } private: - using Struct32 = std::array; - struct GuestCallbacks { VAddr rtld_fini; VAddr rtld_init; @@ -335,7 +259,7 @@ public: explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} { // clang-format off static const FunctionInfo functions[] = { - {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"}, + {0, C<&JITU::CreateJitEnvironment>, "CreateJitEnvironment"}, }; // clang-format on @@ -343,76 +267,33 @@ public: } private: - void CreateJitEnvironment(HLERequestContext& ctx) { - LOG_DEBUG(Service_JIT, "called"); - - struct Parameters { - u64 rx_size; - u64 ro_size; - }; - - IPC::RequestParser rp{ctx}; - const auto parameters{rp.PopRaw()}; - const auto process_handle{ctx.GetCopyHandle(0)}; - const auto rx_mem_handle{ctx.GetCopyHandle(1)}; - const auto ro_mem_handle{ctx.GetCopyHandle(2)}; - - if (parameters.rx_size == 0 || parameters.ro_size == 0) { - LOG_ERROR(Service_JIT, "attempted to init with empty code regions"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto process{ctx.GetObjectFromHandle(process_handle)}; + Result CreateJitEnvironment(Out> out_jit_environment, + u64 rx_size, u64 ro_size, InCopyHandle& process, + InCopyHandle& rx_mem, + InCopyHandle& ro_mem) { if (process.IsNull()) { - LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + LOG_ERROR(Service_JIT, "process is null"); + R_THROW(ResultUnknown); } - - auto rx_mem{ctx.GetObjectFromHandle(rx_mem_handle)}; if (rx_mem.IsNull()) { - LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + LOG_ERROR(Service_JIT, "rx_mem is null"); + R_THROW(ResultUnknown); } - - auto ro_mem{ctx.GetObjectFromHandle(ro_mem_handle)}; - if (ro_mem.IsNull()) { - LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + if (rx_mem.IsNull()) { + LOG_ERROR(Service_JIT, "ro_mem is null"); + R_THROW(ResultUnknown); } CodeMemory rx, ro; - Result res; - res = rx.Initialize(*process, *rx_mem, parameters.rx_size, - Kernel::Svc::MemoryPermission::ReadExecute, generate_random); - if (R_FAILED(res)) { - LOG_ERROR(Service_JIT, "rx_mem could not be mapped for handle=0x{:08X}", rx_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - return; - } + R_TRY(rx.Initialize(*process, *rx_mem, rx_size, Kernel::Svc::MemoryPermission::ReadExecute, + generate_random)); + R_TRY(ro.Initialize(*process, *ro_mem, ro_size, Kernel::Svc::MemoryPermission::Read, + generate_random)); - res = ro.Initialize(*process, *ro_mem, parameters.ro_size, - Kernel::Svc::MemoryPermission::Read, generate_random); - if (R_FAILED(res)) { - LOG_ERROR(Service_JIT, "ro_mem could not be mapped for handle=0x{:08X}", ro_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(res); - return; - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, std::move(process), std::move(rx), - std::move(ro)); + *out_jit_environment = std::make_shared(system, std::move(process), + std::move(rx), std::move(ro)); + R_SUCCEED(); } private: diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index f0658bb5db..ae62c430e7 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -6,13 +6,13 @@ #include "common/scope_exit.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ro/ro.h" #include "core/hle/service/ro/ro_nro_utils.h" #include "core/hle/service/ro/ro_results.h" #include "core/hle/service/ro/ro_types.h" #include "core/hle/service/server_manager.h" -#include "core/hle/service/service.h" namespace Service::RO { @@ -500,46 +500,65 @@ private: } }; -class RoInterface { +class RoInterface : public ServiceFramework { public: - explicit RoInterface(std::shared_ptr ro, NrrKind nrr_kind) - : m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {} + explicit RoInterface(Core::System& system_, const char* name_, std::shared_ptr ro, + NrrKind nrr_kind) + : ServiceFramework{system_, name_}, m_ro(ro), m_context_id(InvalidContextId), + m_nrr_kind(nrr_kind) { + + // clang-format off + static const FunctionInfo functions[] = { + {0, C<&RoInterface::MapManualLoadModuleMemory>, "MapManualLoadModuleMemory"}, + {1, C<&RoInterface::UnmapManualLoadModuleMemory>, "UnmapManualLoadModuleMemory"}, + {2, C<&RoInterface::RegisterModuleInfo>, "RegisterModuleInfo"}, + {3, C<&RoInterface::UnregisterModuleInfo>, "UnregisterModuleInfo"}, + {4, C<&RoInterface::RegisterProcessHandle>, "RegisterProcessHandle"}, + {10, C<&RoInterface::RegisterProcessModuleInfo>, "RegisterProcessModuleInfo"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + ~RoInterface() { m_ro->UnregisterProcess(m_context_id); } - Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address, - u64 nro_size, u64 bss_address, u64 bss_size) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); - R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address, + Result MapManualLoadModuleMemory(Out out_load_address, ClientProcessId client_pid, + u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); + R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address.Get(), m_context_id, nro_address, nro_size, bss_address, bss_size)); } - Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result UnmapManualLoadModuleMemory(ClientProcessId client_pid, u64 nro_address) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address)); } - Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result RegisterModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN( m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true)); } - Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) { - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + Result UnregisterModuleInfo(ClientProcessId client_pid, u64 nrr_address) { + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address)); } - Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) { + Result RegisterProcessHandle(ClientProcessId client_pid, + InCopyHandle& process) { // Register the process. - R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid)); + R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.GetPointerUnsafe(), + *client_pid)); } - Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size, - Kernel::KProcess* process) { + Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, + InCopyHandle& process) { // Validate the process. - R_TRY(m_ro->ValidateProcess(m_context_id, client_pid)); + R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); // Register the module. R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind, @@ -552,137 +571,6 @@ private: NrrKind m_nrr_kind{}; }; -class IRoInterface : public ServiceFramework { -public: - explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr ro, - NrrKind nrr_kind) - : ServiceFramework{system_, name_}, interface { - ro, nrr_kind - } { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"}, - {1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"}, - {2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"}, - {3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"}, - {4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"}, - {10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void MapManualLoadModuleMemory(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nro_address; - u64 nro_size; - u64 bss_address; - u64 bss_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw(); - - u64 load_address = 0; - auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(), - params.nro_address, params.nro_size, - params.bss_address, params.bss_size); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(load_address); - } - - void UnmapManualLoadModuleMemory(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nro_address; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw(); - auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - u64 nrr_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw(); - auto result = - interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void UnregisterModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw(); - auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterProcessHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - auto process = ctx.GetObjectFromHandle(ctx.GetCopyHandle(0)); - auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessHandle(client_pid, process.GetPointerUnsafe()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - void RegisterProcessModuleInfo(HLERequestContext& ctx) { - LOG_DEBUG(Service_LDR, "(called)"); - - struct InputParameters { - u64 client_pid; - u64 nrr_address; - u64 nrr_size; - }; - - IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw(); - auto process = ctx.GetObjectFromHandle(ctx.GetCopyHandle(0)); - - auto client_pid = ctx.GetPID(); - auto result = interface.RegisterProcessModuleInfo( - client_pid, params.nrr_address, params.nrr_size, process.GetPointerUnsafe()); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - RoInterface interface; -}; - } // namespace void LoopProcess(Core::System& system) { @@ -691,11 +579,11 @@ void LoopProcess(Core::System& system) { auto ro = std::make_shared(); const auto RoInterfaceFactoryForUser = [&, ro] { - return std::make_shared(system, "ldr:ro", ro, NrrKind::User); + return std::make_shared(system, "ldr:ro", ro, NrrKind::User); }; const auto RoInterfaceFactoryForJitPlugin = [&, ro] { - return std::make_shared(system, "ro:1", ro, NrrKind::JitPlugin); + return std::make_shared(system, "ro:1", ro, NrrKind::JitPlugin); }; server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser)); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index d539ed0f49..22d1343d5d 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -206,6 +206,22 @@ protected: RegisterHandlersBaseTipc(functions, n); } +protected: + template + void CmifReplyWrap(HLERequestContext& ctx); + + /** + * Wraps the template pointer-to-member function for use in a domain session. + */ + template + static constexpr HandlerFnP D = &Self::template CmifReplyWrap; + + /** + * Wraps the template pointer-to-member function for use in a non-domain session. + */ + template + static constexpr HandlerFnP C = &Self::template CmifReplyWrap; + private: /** * This function is used to allow invocation of pointers to handlers stored in the base class