summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service/nvdrv/devices/ioctl_serialization.h
blob: c560974f1c9dfed5b3b81d8628a0cebdfa6c3fce (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <span>
#include <vector>

#include "common/concepts.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"

namespace Service::Nvidia::Devices {

struct Ioctl1Traits {
    template <typename T, typename R, typename A>
    static T GetClassImpl(R (T::*)(A));

    template <typename T, typename R, typename A>
    static A GetArgImpl(R (T::*)(A));
};

struct Ioctl23Traits {
    template <typename T, typename R, typename A, typename B>
    static T GetClassImpl(R (T::*)(A, B));

    template <typename T, typename R, typename A, typename B>
    static A GetArgImpl(R (T::*)(A, B));
};

template <typename T>
struct ContainerType {
    using ValueType = T;
};

template <Common::IsContiguousContainer T>
struct ContainerType<T> {
    using ValueType = T::value_type;
};

template <typename InnerArg, typename F, typename Self, typename... Rest>
NvResult Wrap(std::span<const u8> input, std::span<u8> output, Self* self, F&& callable,
              Rest&&... rest) {
    using Arg = ContainerType<InnerArg>::ValueType;
    constexpr bool ArgumentIsContainer = Common::IsContiguousContainer<InnerArg>;

    // Verify that the input and output sizes are valid.
    const size_t in_params = input.size() / sizeof(Arg);
    const size_t out_params = output.size() / sizeof(Arg);
    if (in_params * sizeof(Arg) != input.size()) {
        return NvResult::InvalidSize;
    }
    if (out_params * sizeof(Arg) != output.size()) {
        return NvResult::InvalidSize;
    }
    if (in_params == 0 && out_params == 0 && !ArgumentIsContainer) {
        return NvResult::InvalidSize;
    }

    // Copy inputs, if needed.
    std::vector<Arg> params(std::max(in_params, out_params));
    if (in_params > 0) {
        std::memcpy(params.data(), input.data(), input.size());
    }

    // Perform the call.
    NvResult result;
    if constexpr (ArgumentIsContainer) {
        result = (self->*callable)(params, std::forward<Rest>(rest)...);
    } else {
        result = (self->*callable)(params.front(), std::forward<Rest>(rest)...);
    }

    // Copy outputs, if needed.
    if (out_params > 0) {
        std::memcpy(output.data(), params.data(), output.size());
    }

    return result;
}

template <typename F>
NvResult nvdevice::Wrap1(F&& callable, std::span<const u8> input, std::span<u8> output) {
    using Self = decltype(Ioctl1Traits::GetClassImpl(callable));
    using InnerArg = std::remove_reference_t<decltype(Ioctl1Traits::GetArgImpl(callable))>;

    return Wrap<InnerArg>(input, output, static_cast<Self*>(this), callable);
}

template <typename F>
NvResult nvdevice::Wrap2(F&& callable, std::span<const u8> input, std::span<const u8> inline_input,
                         std::span<u8> output) {
    using Self = decltype(Ioctl23Traits::GetClassImpl(callable));
    using InnerArg = std::remove_reference_t<decltype(Ioctl23Traits::GetArgImpl(callable))>;

    return Wrap<InnerArg>(input, output, static_cast<Self*>(this), callable, inline_input);
}

template <typename F>
NvResult nvdevice::Wrap3(F&& callable, std::span<const u8> input, std::span<u8> output,
                         std::span<u8> inline_output) {
    using Self = decltype(Ioctl23Traits::GetClassImpl(callable));
    using InnerArg = std::remove_reference_t<decltype(Ioctl23Traits::GetArgImpl(callable))>;

    return Wrap<InnerArg>(input, output, static_cast<Self*>(this), callable, inline_output);
}

} // namespace Service::Nvidia::Devices