mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-24 00:01:49 +01:00
Implement efficient template based IOCTL deserialisation
This moves IOCTLs from frozen to a template based parser, this allows IOCTL lookup to be performed much more optimally and some IOCTLs to be inlined.
This commit is contained in:
parent
75a67dcfa5
commit
3340c74332
@ -0,0 +1,71 @@
|
|||||||
|
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <common.h>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
namespace skyline::service::nvdrv::deserialisation {
|
||||||
|
template<typename Desc, typename ArgType> requires (Desc::In && IsIn<ArgType>::value)
|
||||||
|
constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset) {
|
||||||
|
auto out{buffer.subspan(offset).template as<ArgType>()};
|
||||||
|
offset += sizeof(ArgType);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Desc, typename ArgType> requires (Desc::Out && Desc::In && IsInOut<ArgType>::value)
|
||||||
|
constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset) {
|
||||||
|
auto &out{buffer.subspan(offset).template as<RemoveInOut<ArgType>>()};
|
||||||
|
offset += sizeof(RemoveInOut<ArgType>);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Desc, typename ArgType> requires (Desc::Out && IsOut<ArgType>::value)
|
||||||
|
constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset) {
|
||||||
|
auto out{Out(buffer.subspan(offset).template as<RemoveOut<ArgType>>())};
|
||||||
|
offset += sizeof(RemoveOut<ArgType>);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates a reference preserving tuple of the given types
|
||||||
|
*/
|
||||||
|
template <typename...Ts>
|
||||||
|
constexpr auto make_ref_tuple(Ts&&...ts) {
|
||||||
|
return std::tuple<Ts...>{std::forward<Ts>(ts)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Desc, typename ArgType, typename... ArgTypes>
|
||||||
|
constexpr auto DecodeArgumentsImpl(span<u8, Desc::Size> buffer, size_t &offset) {
|
||||||
|
if constexpr (IsAutoSizeSpan<ArgType>::value) {
|
||||||
|
// AutoSizeSpan needs to be the last argument
|
||||||
|
static_assert(sizeof...(ArgTypes) == 0);
|
||||||
|
size_t bytes{buffer.size() - offset};
|
||||||
|
size_t extent{bytes / sizeof(RemoveAutoSizeSpan<ArgType>)};
|
||||||
|
return make_ref_tuple(buffer.subspan(offset, extent * sizeof(RemoveAutoSizeSpan<ArgType>)).template cast<RemoveAutoSizeSpan<ArgType>>());
|
||||||
|
} else if constexpr (IsPad<ArgType>::value) {
|
||||||
|
offset += ArgType::Bytes;
|
||||||
|
return DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset);
|
||||||
|
} else {
|
||||||
|
if constexpr(sizeof...(ArgTypes) == 0) {
|
||||||
|
return make_ref_tuple(DecodeArgument<Desc, ArgType>(buffer, offset));
|
||||||
|
} else {
|
||||||
|
return std::tuple_cat(make_ref_tuple(DecodeArgument<Desc, ArgType>(buffer, offset)),
|
||||||
|
DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This fancy thing takes a varadic template of argument types and uses it to deserialise the given buffer
|
||||||
|
* @tparam Desc A MetaIoctlDescriptor or MetaVariableIoctlDescriptor corresponding to the IOCTL that takes these arguments
|
||||||
|
* @return A tuple containing the arguments to be passed to the IOCTL's handler
|
||||||
|
*/
|
||||||
|
template<typename Desc, typename... ArgTypes>
|
||||||
|
constexpr auto DecodeArguments(span<u8, Desc::Size> buffer) {
|
||||||
|
size_t offset{};
|
||||||
|
return DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include "deserialisation.h"
|
||||||
|
|
||||||
|
#define VARIABLE_IOCTL_HANDLER_FUNC(name, cases, variableCases) \
|
||||||
|
PosixResult name::Ioctl(IoctlDescriptor cmd, span<u8> buffer) { \
|
||||||
|
using className = name; \
|
||||||
|
switch (cmd.raw) { \
|
||||||
|
cases; \
|
||||||
|
default: \
|
||||||
|
cmd.size = 0; \
|
||||||
|
switch (cmd.raw) { \
|
||||||
|
variableCases; \
|
||||||
|
default: \
|
||||||
|
return PosixResult::InappropriateIoctlForDevice; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IOCTL_HANDLER_FUNC(name, cases) \
|
||||||
|
PosixResult name::Ioctl(IoctlDescriptor cmd, span<u8> buffer) { \
|
||||||
|
using className = name; \
|
||||||
|
switch (cmd.raw) { \
|
||||||
|
cases; \
|
||||||
|
default: \
|
||||||
|
return PosixResult::InappropriateIoctlForDevice; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IOCTL_CASE_ARGS_I(out, in, size, magic, function, name, ...) \
|
||||||
|
case MetaIoctlDescriptor<out, in, size, magic, function>::Raw(): { \
|
||||||
|
using IoctlType = MetaIoctlDescriptor< out, in, size, magic, function>; \
|
||||||
|
auto args = DecodeArguments<IoctlType, __VA_ARGS__>(buffer.subspan<0, size>()); \
|
||||||
|
return std::apply(&className::name, std::tuple_cat(std::make_tuple(this), args)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IOCTL_CASE_NOARGS_I(out, in, size, magic, function, name) \
|
||||||
|
case MetaIoctlDescriptor<out, in, size, magic, function>::Raw(): \
|
||||||
|
return className::name();
|
||||||
|
|
||||||
|
#define IOCTL_CASE_RESULT_I(out, in, size, magic, function, result) \
|
||||||
|
case MetaIoctlDescriptor<out, in, size, magic, function>::Raw(): \
|
||||||
|
return result;
|
||||||
|
|
||||||
|
#define IOCTL_CASE_ARGS(...) IOCTL_CASE_ARGS_I(__VA_ARGS__)
|
||||||
|
#define IOCTL_CASE_NOARGS(...) IOCTL_CASE_NOARGS_I(__VA_ARGS__)
|
||||||
|
#define IOCTL_CASE_RESULT(...) IOCTL_CASE_RESULT_I(__VA_ARGS__)
|
||||||
|
|
||||||
|
#define VARIABLE_IOCTL_CASE_ARGS_I(out, in, magic, function, name, ...) \
|
||||||
|
case MetaVariableIoctlDescriptor<out, in, magic, function>::Raw(): { \
|
||||||
|
using IoctlType = MetaVariableIoctlDescriptor<out, in, magic, function>; \
|
||||||
|
auto args = DecodeArguments<IoctlType, __VA_ARGS__>(buffer); \
|
||||||
|
return std::apply(&className::name, std::tuple_cat(std::make_tuple(this), args)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VARIABLE_IOCTL_CASE_ARGS(...) VARIABLE_IOCTL_CASE_ARGS_I(__VA_ARGS__)
|
||||||
|
|
||||||
|
#define IN false, true
|
||||||
|
#define OUT true, false
|
||||||
|
#define INOUT true, true
|
||||||
|
#define NONE false, false
|
||||||
|
#define SIZE(size) size
|
||||||
|
#define FUNC(func) func
|
||||||
|
#define MAGIC(magic) magic
|
||||||
|
#define ARGS(...) __VA_ARGS__
|
@ -0,0 +1,21 @@
|
|||||||
|
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#undef IOCTL_HANDLER_FUNC
|
||||||
|
#undef VARIABLE_IOCTL_HANDLER_FUNC
|
||||||
|
#undef IOCTL_CASE_ARGS_I
|
||||||
|
#undef IOCTL_CASE_NOARGS_I
|
||||||
|
#undef IOCTL_CASE_RESULT_I
|
||||||
|
#undef IOCTL_CASE_ARGS
|
||||||
|
#undef IOCTL_CASE_NOARGS
|
||||||
|
#undef IOCTL_CASE_RESULT
|
||||||
|
#undef VARIABLE_IOCTL_CASE_ARGS_I
|
||||||
|
#undef VARIABLE_IOCTL_CASE_ARGS
|
||||||
|
#undef IN
|
||||||
|
#undef OUT
|
||||||
|
#undef INOUT
|
||||||
|
#undef NONE
|
||||||
|
#undef SIZE
|
||||||
|
#undef FUNC
|
||||||
|
#undef MAGIC
|
||||||
|
#undef ARGS
|
@ -0,0 +1,140 @@
|
|||||||
|
// SPDX-License-Identifier: MIT OR MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <common.h>
|
||||||
|
#include <services/nvdrv/types.h>
|
||||||
|
|
||||||
|
namespace skyline::service::nvdrv::deserialisation {
|
||||||
|
// IOCTL parameters can't be pointers and must be trivially copyable
|
||||||
|
template<typename T>
|
||||||
|
concept BufferData = std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>;
|
||||||
|
|
||||||
|
// Input IOCTL template types
|
||||||
|
template<typename T> requires BufferData<T>
|
||||||
|
using In = const T;
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
struct IsIn : std::false_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct IsIn<In<T>> : std::true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// Input/Output IOCTL template types
|
||||||
|
template<typename T> requires BufferData<T>
|
||||||
|
using InOut = T &;
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
struct IsInOut : std::false_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct IsInOut<InOut<T>> : std::true_type {};
|
||||||
|
|
||||||
|
template<typename T> requires IsInOut<T>::value
|
||||||
|
using RemoveInOut = typename std::remove_reference<T>::type;
|
||||||
|
|
||||||
|
|
||||||
|
// Output IOCTL template types
|
||||||
|
template<typename T> requires BufferData<T>
|
||||||
|
class Out {
|
||||||
|
private:
|
||||||
|
T &ref;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Out(T &ref) : ref(ref) {}
|
||||||
|
|
||||||
|
using ValueType = T;
|
||||||
|
|
||||||
|
Out& operator=(T val) {
|
||||||
|
ref = std::move(val);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
struct IsOut : std::false_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct IsOut<Out<T>> : std::true_type {};
|
||||||
|
|
||||||
|
template<typename T> requires IsOut<T>::value
|
||||||
|
using RemoveOut = typename T::ValueType;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T> requires BufferData<T>
|
||||||
|
using AutoSizeSpan = span<T>;
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
struct IsAutoSizeSpan : std::false_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct IsAutoSizeSpan<AutoSizeSpan<T>> : std::true_type {};
|
||||||
|
|
||||||
|
template<typename T> requires IsAutoSizeSpan<T>::value
|
||||||
|
using RemoveAutoSizeSpan = typename T::element_type;
|
||||||
|
|
||||||
|
// Padding template type
|
||||||
|
template<typename T, size_t Count = 1> requires BufferData<T>
|
||||||
|
struct Pad {
|
||||||
|
static constexpr auto Bytes{Count * sizeof(T)};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
struct IsPad : std::false_type {};
|
||||||
|
|
||||||
|
template<typename T, size_t TCount>
|
||||||
|
struct IsPad<Pad<T, TCount>> : std::true_type {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes an IOCTL as a type for use in deserialisation
|
||||||
|
*/
|
||||||
|
template <bool TOut, bool TIn, u16 TSize, i8 TMagic, u8 TFunction>
|
||||||
|
struct MetaIoctlDescriptor {
|
||||||
|
static constexpr auto Out{TOut};
|
||||||
|
static constexpr auto In{TIn};
|
||||||
|
static constexpr auto Size{TSize};
|
||||||
|
static constexpr auto Magic{TMagic};
|
||||||
|
static constexpr auto Function{TFunction};
|
||||||
|
|
||||||
|
constexpr operator IoctlDescriptor() const {
|
||||||
|
return {Out, In, Size, Magic, Function};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr static u32 Raw() {
|
||||||
|
u32 raw{Function};
|
||||||
|
|
||||||
|
i8 offset{8};
|
||||||
|
raw |= Magic << offset; offset += 8;
|
||||||
|
raw |= Size << offset; offset += 14;
|
||||||
|
raw |= In << offset; offset++;
|
||||||
|
raw |= Out << offset; offset++;
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Describes a variable length IOCTL as a type for use in deserialisation
|
||||||
|
*/
|
||||||
|
template <bool TOut, bool TIn, i8 TMagic, u8 TFunction>
|
||||||
|
struct MetaVariableIoctlDescriptor {
|
||||||
|
static constexpr auto Out{TOut};
|
||||||
|
static constexpr auto In{TIn};
|
||||||
|
static constexpr auto Size{std::dynamic_extent};
|
||||||
|
static constexpr auto Magic{TMagic};
|
||||||
|
static constexpr auto Function{TFunction};
|
||||||
|
|
||||||
|
constexpr static u32 Raw() {
|
||||||
|
u32 raw{Function};
|
||||||
|
|
||||||
|
i8 offset{8};
|
||||||
|
raw |= Magic << offset; offset += 8;
|
||||||
|
offset += 14; // Use a 0 size for raw
|
||||||
|
raw |= In << offset; offset++;
|
||||||
|
raw |= Out << offset; offset++;
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user