mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 17:51:52 +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