mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-10-31 10:44:49 +01:00 
			
		
		
		
	service: add template serializer for method calls
This commit is contained in:
		
							parent
							
								
									a76f6a2775
								
							
						
					
					
						commit
						01a2d978eb
					
				| @ -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 | ||||
|  | ||||
							
								
								
									
										337
									
								
								src/core/hle/service/cmif_serialization.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										337
									
								
								src/core/hle/service/cmif_serialization.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 <ArgumentType Type1, ArgumentType Type2, typename MethodArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t ArgIndex = 0> | ||||
| constexpr u32 GetArgumentRawDataSize() { | ||||
|     if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) { | ||||
|         return static_cast<u32>(DataOffset); | ||||
|     } else { | ||||
|         using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; | ||||
| 
 | ||||
|         if constexpr (ArgumentTraits<ArgType>::Type == Type1 || ArgumentTraits<ArgType>::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<Type1, Type2, MethodArguments, ArgAlign, ArgEnd, ArgIndex + 1>(); | ||||
|         } else { | ||||
|             return GetArgumentRawDataSize<Type1, Type2, MethodArguments, PrevAlign, DataOffset, ArgIndex + 1>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <ArgumentType DataType, typename MethodArguments, size_t ArgCount = 0, size_t ArgIndex = 0> | ||||
| constexpr u32 GetArgumentTypeCount() { | ||||
|     if constexpr (ArgIndex >= std::tuple_size_v<MethodArguments>) { | ||||
|         return static_cast<u32>(ArgCount); | ||||
|     } else { | ||||
|         using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; | ||||
| 
 | ||||
|         if constexpr (ArgumentTraits<ArgType>::Type == DataType) { | ||||
|             return GetArgumentTypeCount<DataType, MethodArguments, ArgCount + 1, ArgIndex + 1>(); | ||||
|         } else { | ||||
|             return GetArgumentTypeCount<DataType, MethodArguments, ArgCount, ArgIndex + 1>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename MethodArguments> | ||||
| constexpr RequestLayout GetNonDomainReplyInLayout() { | ||||
|     return RequestLayout{ | ||||
|         .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(), | ||||
|         .move_handle_count = 0, | ||||
|         .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(), | ||||
|         .domain_interface_count = 0, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| template <typename MethodArguments> | ||||
| constexpr RequestLayout GetDomainReplyInLayout() { | ||||
|     return RequestLayout{ | ||||
|         .copy_handle_count = GetArgumentTypeCount<ArgumentType::InCopyHandle, MethodArguments>(), | ||||
|         .move_handle_count = 0, | ||||
|         .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::InData, ArgumentType::InProcessId, MethodArguments>(), | ||||
|         .domain_interface_count = GetArgumentTypeCount<ArgumentType::InInterface, MethodArguments>(), | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| template <typename MethodArguments> | ||||
| constexpr RequestLayout GetNonDomainReplyOutLayout() { | ||||
|     return RequestLayout{ | ||||
|         .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(), | ||||
|         .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>() + GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(), | ||||
|         .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(), | ||||
|         .domain_interface_count = 0, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| template <typename MethodArguments> | ||||
| constexpr RequestLayout GetDomainReplyOutLayout() { | ||||
|     return RequestLayout{ | ||||
|         .copy_handle_count = GetArgumentTypeCount<ArgumentType::OutCopyHandle, MethodArguments>(), | ||||
|         .move_handle_count = GetArgumentTypeCount<ArgumentType::OutMoveHandle, MethodArguments>(), | ||||
|         .cmif_raw_data_size = GetArgumentRawDataSize<ArgumentType::OutData, ArgumentType::OutData, MethodArguments>(), | ||||
|         .domain_interface_count = GetArgumentTypeCount<ArgumentType::OutInterface, MethodArguments>(), | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| template <bool Domain, typename MethodArguments> | ||||
| constexpr RequestLayout GetReplyInLayout() { | ||||
|     return Domain ? GetDomainReplyInLayout<MethodArguments>() : GetNonDomainReplyInLayout<MethodArguments>(); | ||||
| } | ||||
| 
 | ||||
| template <bool Domain, typename MethodArguments> | ||||
| constexpr RequestLayout GetReplyOutLayout() { | ||||
|     return Domain ? GetDomainReplyOutLayout<MethodArguments>() : GetNonDomainReplyOutLayout<MethodArguments>(); | ||||
| } | ||||
| 
 | ||||
| using OutTemporaryBuffers = std::array<Common::ScratchBuffer<u8>, 3>; | ||||
| 
 | ||||
| template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t HandleIndex = 0, size_t InBufferIndex = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0> | ||||
| void ReadInArgument(CallArguments& args, const u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { | ||||
|     if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) { | ||||
|         return; | ||||
|     } else { | ||||
|         using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; | ||||
| 
 | ||||
|         if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InData || ArgumentTraits<ArgType>::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<ArgType>::Type == ArgumentType::InProcessId) { | ||||
|                 // TODO: abort parsing if PID is not provided?
 | ||||
|                 // TODO: validate against raw data value?
 | ||||
|                 std::get<ArgIndex>(args).pid = ctx.GetPID(); | ||||
|             } else { | ||||
|                 std::memcpy(&std::get<ArgIndex>(args), raw_data + ArgOffset, ArgSize); | ||||
|             } | ||||
| 
 | ||||
|             return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::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<ArgIndex>(args) = ctx.GetDomainHandler<ArgType::Type>(value - 1); | ||||
| 
 | ||||
|             return ReadInArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, HandleIndex, InBufferIndex, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InCopyHandle) { | ||||
|             std::get<ArgIndex>(args) = std::move(ctx.GetObjectFromHandle<typename ArgType::Type>(ctx.GetCopyHandle(HandleIndex))); | ||||
| 
 | ||||
|             return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex + 1, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InLargeData) { | ||||
|             constexpr size_t BufferSize = sizeof(ArgType); | ||||
| 
 | ||||
|             // Clear the existing data.
 | ||||
|             std::memset(&std::get<ArgIndex>(args), 0, BufferSize); | ||||
| 
 | ||||
|             std::span<const u8> 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<ArgIndex>(args), buffer.data(), std::min(BufferSize, buffer.size())); | ||||
| 
 | ||||
|             return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::InBuffer) { | ||||
|             using ElementType = typename ArgType::Type; | ||||
| 
 | ||||
|             std::span<const u8> 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<ArgIndex>(args) = std::span(ptr, size); | ||||
| 
 | ||||
|             return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex + 1, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) { | ||||
|             constexpr size_t BufferSize = sizeof(ArgType); | ||||
| 
 | ||||
|             // Clear the existing data.
 | ||||
|             std::memset(&std::get<ArgIndex>(args), 0, BufferSize); | ||||
| 
 | ||||
|             return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::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<ArgIndex>(args) = std::span(ptr, size); | ||||
| 
 | ||||
|             return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else { | ||||
|             return ReadInArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, HandleIndex, InBufferIndex, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <bool Domain, typename MethodArguments, typename CallArguments, size_t PrevAlign = 1, size_t DataOffset = 0, size_t OutBufferIndex = 0, bool RawDataFinished = false, size_t ArgIndex = 0> | ||||
| void WriteOutArgument(CallArguments& args, u8* raw_data, HLERequestContext& ctx, OutTemporaryBuffers& temp) { | ||||
|     if constexpr (ArgIndex >= std::tuple_size_v<CallArguments>) { | ||||
|         return; | ||||
|     } else { | ||||
|         using ArgType = std::tuple_element_t<ArgIndex, MethodArguments>; | ||||
| 
 | ||||
|         if constexpr (ArgumentTraits<ArgType>::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<ArgIndex>(args), ArgSize); | ||||
| 
 | ||||
|             return WriteOutArgument<Domain, MethodArguments, CallArguments, ArgAlign, ArgEnd, OutBufferIndex, false, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutInterface) { | ||||
|             if constexpr (Domain) { | ||||
|                 ctx.AddDomainObject(std::get<ArgIndex>(args)); | ||||
|             } else { | ||||
|                 ctx.AddMoveInterface(std::get<ArgIndex>(args)); | ||||
|             } | ||||
| 
 | ||||
|             return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, true, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutCopyHandle) { | ||||
|             ctx.AddCopyObject(std::get<ArgIndex>(args).GetPointerUnsafe()); | ||||
| 
 | ||||
|             return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutMoveHandle) { | ||||
|             ctx.AddMoveObject(std::get<ArgIndex>(args).GetPointerUnsafe()); | ||||
| 
 | ||||
|             return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::Type == ArgumentType::OutLargeData) { | ||||
|             constexpr size_t BufferSize = sizeof(ArgType); | ||||
| 
 | ||||
|             ASSERT(ctx.CanWriteBuffer(OutBufferIndex)); | ||||
|             if constexpr (ArgType::Attr & BufferAttr_HipcAutoSelect) { | ||||
|                 ctx.WriteBuffer(std::get<ArgIndex>(args), OutBufferIndex); | ||||
|             } else if constexpr (ArgType::Attr & BufferAttr_HipcMapAlias) { | ||||
|                 ctx.WriteBufferB(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex); | ||||
|             } else /* if (ArgType::Attr & BufferAttr_HipcPointer) */ { | ||||
|                 ctx.WriteBufferC(&std::get<ArgIndex>(args), BufferSize, OutBufferIndex); | ||||
|             } | ||||
| 
 | ||||
|             return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } else if constexpr (ArgumentTraits<ArgType>::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<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex + 1, RawDataFinished, ArgIndex + 1>( args, raw_data, ctx, temp); | ||||
|         } else { | ||||
|             return WriteOutArgument<Domain, MethodArguments, CallArguments, PrevAlign, DataOffset, OutBufferIndex, RawDataFinished, ArgIndex + 1>(args, raw_data, ctx, temp); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <bool Domain, typename T, typename... A> | ||||
| 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<std::remove_reference_t<A>...>; | ||||
| 
 | ||||
|     OutTemporaryBuffers buffers{}; | ||||
|     auto call_arguments = std::tuple<typename RemoveOut<A>::Type...>(); | ||||
| 
 | ||||
|     // Read inputs.
 | ||||
|     const size_t offset_plus_command_id = ctx.GetDataPayloadOffset() + 2; | ||||
|     ReadInArgument<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + offset_plus_command_id), ctx, buffers); | ||||
| 
 | ||||
|     // Call.
 | ||||
|     const auto Callable = [&]<typename... CallArgs>(CallArgs&... args) { | ||||
|         return (t.*f)(args...); | ||||
|     }; | ||||
|     const Result res = std::apply(Callable, call_arguments); | ||||
| 
 | ||||
|     // Write result.
 | ||||
|     constexpr RequestLayout layout = GetReplyOutLayout<Domain, MethodArguments>(); | ||||
|     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<Domain, MethodArguments>(call_arguments, reinterpret_cast<u8*>(ctx.CommandBuffer() + rb.GetCurrentOffset()), ctx, buffers); | ||||
| } | ||||
| // clang-format on
 | ||||
| 
 | ||||
| template <typename Self> | ||||
| template <bool Domain, auto F> | ||||
| inline void ServiceFramework<Self>::CmifReplyWrap(HLERequestContext& ctx) { | ||||
|     return CmifReplyWrapImpl<Domain>(ctx, *static_cast<Self*>(this), F); | ||||
| } | ||||
| 
 | ||||
| } // namespace Service
 | ||||
							
								
								
									
										234
									
								
								src/core/hle/service/cmif_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/core/hle/service/cmif_types.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,234 @@ | ||||
| // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 | ||||
| // SPDX-License-Identifier: GPL-2.0-or-later
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "common/common_funcs.h" | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/service/hle_ipc.h" | ||||
| 
 | ||||
| namespace Service { | ||||
| 
 | ||||
| // clang-format off
 | ||||
| template <typename T> | ||||
| class Out { | ||||
| public: | ||||
|     /* implicit */ Out(T& t) : raw(&t) {} | ||||
|     ~Out() = default; | ||||
| 
 | ||||
|     T* Get() const { | ||||
|         return raw; | ||||
|     } | ||||
| 
 | ||||
|     T& operator*() { | ||||
|         return *raw; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     T* raw; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| using SharedPointer = std::shared_ptr<T>; | ||||
| 
 | ||||
| struct ClientProcessId { | ||||
|     explicit operator bool() const { | ||||
|         return pid != 0; | ||||
|     } | ||||
| 
 | ||||
|     const u64& operator*() const { | ||||
|         return pid; | ||||
|     } | ||||
| 
 | ||||
|     u64 pid; | ||||
| }; | ||||
| 
 | ||||
| using ClientAppletResourceUserId = ClientProcessId; | ||||
| 
 | ||||
| template <typename T> | ||||
| class InCopyHandle : public Kernel::KScopedAutoObject<T> { | ||||
| public: | ||||
|     using Type = T; | ||||
| 
 | ||||
|     template <typename... Args> | ||||
|     /* implicit */ InCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {} | ||||
|     ~InCopyHandle() = default; | ||||
| 
 | ||||
|     InCopyHandle& operator=(InCopyHandle&& rhs) { | ||||
|         Kernel::KScopedAutoObject<T>::operator=(std::move(rhs)); | ||||
|         return *this; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| class OutCopyHandle : public Kernel::KScopedAutoObject<T> { | ||||
| public: | ||||
|     using Type = T; | ||||
| 
 | ||||
|     template <typename... Args> | ||||
|     /* implicit */ OutCopyHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {} | ||||
|     ~OutCopyHandle() = default; | ||||
| 
 | ||||
|     OutCopyHandle& operator=(OutCopyHandle&& rhs) { | ||||
|         Kernel::KScopedAutoObject<T>::operator=(std::move(rhs)); | ||||
|         return *this; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| class OutMoveHandle : public Kernel::KScopedAutoObject<T> { | ||||
| public: | ||||
|     using Type = T; | ||||
| 
 | ||||
|     template <typename... Args> | ||||
|     /* implicit */ OutMoveHandle(Args&&... args) : Kernel::KScopedAutoObject<T>(std::forward<Args...>(args)...) {} | ||||
|     ~OutMoveHandle() = default; | ||||
| 
 | ||||
|     OutMoveHandle& operator=(OutMoveHandle&& rhs) { | ||||
|         Kernel::KScopedAutoObject<T>::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 <typename T, int A> | ||||
| struct Buffer : public std::span<T> { | ||||
|     static_assert(std::is_trivial_v<T>, "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<BufferAttr>(A); | ||||
|     using Type = T; | ||||
| 
 | ||||
|     Buffer& operator=(const std::span<T>& rhs) { | ||||
|         std::span<T>::operator=(rhs); | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     T& operator*() const { | ||||
|         return *this->data(); | ||||
|     } | ||||
| 
 | ||||
|     explicit operator bool() const { | ||||
|         return this->size() > 0; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template <BufferAttr A> | ||||
| using InBuffer = Buffer<const u8, BufferAttr_In | A>; | ||||
| 
 | ||||
| template <typename T, BufferAttr A> | ||||
| using InArray = Buffer<T, BufferAttr_In | A>; | ||||
| 
 | ||||
| template <BufferAttr A> | ||||
| using OutBuffer = Buffer<u8, BufferAttr_Out | A>; | ||||
| 
 | ||||
| template <typename T, BufferAttr A> | ||||
| using OutArray = Buffer<T, BufferAttr_Out | A>; | ||||
| 
 | ||||
| template <typename T, int A> | ||||
| struct LargeData : public T { | ||||
|     static_assert(std::is_trivial_v<T>, "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<BufferAttr>(A); | ||||
|     using Type = T; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, BufferAttr A> | ||||
| using InLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_In | A>; | ||||
| 
 | ||||
| template <typename T, BufferAttr A> | ||||
| using OutLargeData = LargeData<T, BufferAttr_FixedSize | BufferAttr_Out | A>; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct RemoveOut { | ||||
|     using Type = std::remove_reference_t<T>; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct RemoveOut<Out<T>> { | ||||
|     using Type = T; | ||||
| }; | ||||
| 
 | ||||
| enum class ArgumentType { | ||||
|     InProcessId, | ||||
|     InData, | ||||
|     InInterface, | ||||
|     InCopyHandle, | ||||
|     OutData, | ||||
|     OutInterface, | ||||
|     OutCopyHandle, | ||||
|     OutMoveHandle, | ||||
|     InBuffer, | ||||
|     InLargeData, | ||||
|     OutBuffer, | ||||
|     OutLargeData, | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct ArgumentTraits; | ||||
| 
 | ||||
| template <> | ||||
| struct ArgumentTraits<ClientProcessId> { | ||||
|     static constexpr ArgumentType Type = ArgumentType::InProcessId; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct ArgumentTraits<SharedPointer<T>> { | ||||
|     static constexpr ArgumentType Type = ArgumentType::InInterface; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct ArgumentTraits<InCopyHandle<T>> { | ||||
|     static constexpr ArgumentType Type = ArgumentType::InCopyHandle; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct ArgumentTraits<Out<SharedPointer<T>>> { | ||||
|     static constexpr ArgumentType Type = ArgumentType::OutInterface; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct ArgumentTraits<Out<T>> { | ||||
|     static constexpr ArgumentType Type = ArgumentType::OutData; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct ArgumentTraits<OutCopyHandle<T>> { | ||||
|     static constexpr ArgumentType Type = ArgumentType::OutCopyHandle; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct ArgumentTraits<OutMoveHandle<T>> { | ||||
|     static constexpr ArgumentType Type = ArgumentType::OutMoveHandle; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, int A> | ||||
| struct ArgumentTraits<Buffer<T, A>> { | ||||
|     static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutBuffer : ArgumentType::InBuffer; | ||||
| }; | ||||
| 
 | ||||
| template <typename T, int A> | ||||
| struct ArgumentTraits<LargeData<T, A>> { | ||||
|     static constexpr ArgumentType Type = (A & BufferAttr_In) == 0 ? ArgumentType::OutLargeData : ArgumentType::InLargeData; | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| struct ArgumentTraits { | ||||
|     static constexpr ArgumentType Type = ArgumentType::InData; | ||||
| }; | ||||
| // clang-format on
 | ||||
| 
 | ||||
| } // namespace Service
 | ||||
| @ -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<Service::SessionRequestManager>(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"; | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
|  | ||||
| @ -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<u64, 4>; | ||||
| static_assert(sizeof(Struct32) == 32, "Struct32 has wrong size"); | ||||
| 
 | ||||
| class IJitEnvironment final : public ServiceFramework<IJitEnvironment> { | ||||
| 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<CodeRange, 2> ranges; | ||||
|             Struct32 data; | ||||
|         }; | ||||
| 
 | ||||
|         struct OutputParameters { | ||||
|             s32 return_value; | ||||
|             std::array<CodeRange, 2> ranges; | ||||
|         }; | ||||
| 
 | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         const auto parameters{rp.PopRaw<InputParameters>()}; | ||||
| 
 | ||||
|         // Optional input/output buffers
 | ||||
|         const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()}; | ||||
|         std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); | ||||
| 
 | ||||
|     Result GenerateCode(Out<s32> out_return_value, Out<CodeRange> out_range0, | ||||
|                         Out<CodeRange> out_range1, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, | ||||
|                         u32 data_size, u64 command, CodeRange range0, CodeRange range1, | ||||
|                         Struct32 data, InBuffer<BufferAttr_HipcMapAlias> 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<s32>(ret_ptr)}; | ||||
|         *out_return_value = context.GetHeap<s32>(ret_ptr); | ||||
|         *out_range0 = context.GetHeap<CodeRange>(c0_out_ptr); | ||||
|         *out_range1 = context.GetHeap<CodeRange>(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<CodeRange>(c0_out_ptr), | ||||
|                         context.GetHeap<CodeRange>(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<u64>()}; | ||||
| 
 | ||||
|         // Optional input/output buffers
 | ||||
|         const auto input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::span<const u8>()}; | ||||
|         std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0); | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
| 
 | ||||
|     Result Control(Out<s32> out_return_value, InBuffer<BufferAttr_HipcMapAlias> in_data, | ||||
|                    OutBuffer<BufferAttr_HipcMapAlias> 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<s32>(ret_ptr)}; | ||||
|         *out_return_value = context.GetHeap<s32>(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<u64>()}; | ||||
|         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<Kernel::KTransferMemory>(tmem_handle)}; | ||||
|     Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem, | ||||
|                       InBuffer<BufferAttr_HipcMapAlias> nrr, | ||||
|                       InBuffer<BufferAttr_HipcMapAlias> 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<u64> rx_offset, Out<u64> 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<u8, 32>; | ||||
| 
 | ||||
|     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<Parameters>()}; | ||||
|         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<Kernel::KProcess>(process_handle)}; | ||||
|     Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment, | ||||
|                                 u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process, | ||||
|                                 InCopyHandle<Kernel::KCodeMemory>& rx_mem, | ||||
|                                 InCopyHandle<Kernel::KCodeMemory>& 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<Kernel::KCodeMemory>(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<Kernel::KCodeMemory>(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<IJitEnvironment>(system, std::move(process), std::move(rx), | ||||
|                                              std::move(ro)); | ||||
|         *out_jit_environment = std::make_shared<IJitEnvironment>(system, std::move(process), | ||||
|                                                                  std::move(rx), std::move(ro)); | ||||
|         R_SUCCEED(); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|  | ||||
| @ -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<RoInterface> { | ||||
| public: | ||||
|     explicit RoInterface(std::shared_ptr<RoContext> 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<RoContext> 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<u64> 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<Kernel::KProcess>& 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<Kernel::KProcess>& 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<IRoInterface> { | ||||
| public: | ||||
|     explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> 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<InputParameters>(); | ||||
| 
 | ||||
|         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<InputParameters>(); | ||||
|         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<InputParameters>(); | ||||
|         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<InputParameters>(); | ||||
|         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<Kernel::KProcess>(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<InputParameters>(); | ||||
|         auto process = ctx.GetObjectFromHandle<Kernel::KProcess>(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<RoContext>(); | ||||
| 
 | ||||
|     const auto RoInterfaceFactoryForUser = [&, ro] { | ||||
|         return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User); | ||||
|         return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User); | ||||
|     }; | ||||
| 
 | ||||
|     const auto RoInterfaceFactoryForJitPlugin = [&, ro] { | ||||
|         return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); | ||||
|         return std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin); | ||||
|     }; | ||||
| 
 | ||||
|     server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser)); | ||||
|  | ||||
| @ -206,6 +206,22 @@ protected: | ||||
|         RegisterHandlersBaseTipc(functions, n); | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     template <bool Domain, auto F> | ||||
|     void CmifReplyWrap(HLERequestContext& ctx); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Wraps the template pointer-to-member function for use in a domain session. | ||||
|      */ | ||||
|     template <auto F> | ||||
|     static constexpr HandlerFnP<Self> D = &Self::template CmifReplyWrap<true, F>; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Wraps the template pointer-to-member function for use in a non-domain session. | ||||
|      */ | ||||
|     template <auto F> | ||||
|     static constexpr HandlerFnP<Self> C = &Self::template CmifReplyWrap<false, F>; | ||||
| 
 | ||||
| private: | ||||
|     /**
 | ||||
|      * This function is used to allow invocation of pointers to handlers stored in the base class | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user