Merge pull request #12783 from liamwhite/cmif-generation
service: add template serializer for method calls
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user