Introduce variable sized span support to nvdrv deserialisation

The element containing the size first needs to be saved to a save slot with Save<T, slotId>, it can then be read back later as the size of a span with SlotSizeSpan<T, slotId>. This is needed to support the Host1XChannel Submit IOCTL.
This commit is contained in:
Billy Laws 2021-10-23 14:36:33 +01:00 committed by PixelyIon
parent 6eeaa343f8
commit 386a3447a8
3 changed files with 75 additions and 24 deletions

View File

@ -33,9 +33,9 @@ namespace skyline {
template<typename Traits> template<typename Traits>
constexpr span(const std::basic_string_view<T, Traits> &string) : std::span<T, Extent>(const_cast<T *>(string.data()), string.size()) {} constexpr span(const std::basic_string_view<T, Traits> &string) : std::span<T, Extent>(const_cast<T *>(string.data()), string.size()) {}
template<typename Out> template<typename Out, bool SkipSizeCheck = false>
constexpr Out &as() { constexpr Out &as() {
if constexpr (Extent != std::dynamic_extent && sizeof(T) * Extent >= sizeof(Out)) if constexpr (SkipSizeCheck || (Extent != std::dynamic_extent && sizeof(T) * Extent >= sizeof(Out)))
return *reinterpret_cast<Out *>(span::data()); return *reinterpret_cast<Out *>(span::data());
if (span::size_bytes() >= sizeof(Out)) if (span::size_bytes() >= sizeof(Out))

View File

@ -9,26 +9,37 @@
namespace skyline::service::nvdrv::deserialisation { namespace skyline::service::nvdrv::deserialisation {
template<typename Desc, typename ArgType> requires (Desc::In && IsIn<ArgType>::value) template<typename Desc, typename ArgType> requires (Desc::In && IsIn<ArgType>::value)
constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset) { constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset, std::array<size_t, NumSaveSlots> &saveSlots) {
auto out{buffer.subspan(offset).template as<ArgType>()}; auto out{buffer.subspan(offset).template as<ArgType, true>()};
offset += sizeof(ArgType); offset += sizeof(ArgType);
return out; return out;
} }
template<typename Desc, typename ArgType> requires (Desc::Out && Desc::In && IsInOut<ArgType>::value) template<typename Desc, typename ArgType> requires (Desc::Out && Desc::In && IsInOut<ArgType>::value)
constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset) { constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset, std::array<size_t, NumSaveSlots> &saveSlots) {
auto &out{buffer.subspan(offset).template as<RemoveInOut<ArgType>>()}; auto &out{buffer.subspan(offset).template as<RemoveInOut<ArgType>, true>()};
offset += sizeof(RemoveInOut<ArgType>); offset += sizeof(RemoveInOut<ArgType>);
return out; return out;
} }
template<typename Desc, typename ArgType> requires (Desc::Out && IsOut<ArgType>::value) template<typename Desc, typename ArgType> requires (Desc::Out && IsOut<ArgType>::value)
constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset) { constexpr ArgType DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset, std::array<size_t, NumSaveSlots> &saveSlots) {
auto out{Out(buffer.subspan(offset).template as<RemoveOut<ArgType>>())}; auto out{Out(buffer.subspan(offset).template as<RemoveOut<ArgType>, true>())};
offset += sizeof(RemoveOut<ArgType>); offset += sizeof(RemoveOut<ArgType>);
return out; return out;
} }
template<typename Desc, typename ArgType> requires (IsSlotSizeSpan<ArgType>::value)
constexpr auto DecodeArgument(span<u8, Desc::Size> buffer, size_t &offset, std::array<size_t, NumSaveSlots> &saveSlots) {
size_t bytes{saveSlots[ArgType::SaveSlot] * sizeof(RemoveSlotSizeSpan<ArgType>)};
auto out{buffer.subspan(offset, bytes).template cast<RemoveSlotSizeSpan<ArgType>, std::dynamic_extent, true>()};
offset += bytes;
// Return a simple `span` as that will be the function argument type as opposed to `SlotSizeSpan`
return out;
}
/** /**
* @brief Creates a reference preserving tuple of the given types * @brief Creates a reference preserving tuple of the given types
*/ */
@ -37,23 +48,27 @@ namespace skyline::service::nvdrv::deserialisation {
return std::tuple<Ts...>{std::forward<Ts>(ts)...}; return std::tuple<Ts...>{std::forward<Ts>(ts)...};
} }
template<typename Desc, typename ArgType, typename... ArgTypes> template<typename Desc, typename ArgType, typename... ArgTypes>
constexpr auto DecodeArgumentsImpl(span<u8, Desc::Size> buffer, size_t &offset) { constexpr auto DecodeArgumentsImpl(span<u8, Desc::Size> buffer, size_t &offset, std::array<size_t, NumSaveSlots> &saveSlots) {
if constexpr (IsAutoSizeSpan<ArgType>::value) { if constexpr (IsAutoSizeSpan<ArgType>::value) {
// AutoSizeSpan needs to be the last argument // AutoSizeSpan needs to be the last argument
static_assert(sizeof...(ArgTypes) == 0); static_assert(sizeof...(ArgTypes) == 0);
size_t bytes{buffer.size() - offset}; return make_ref_tuple(buffer.subspan(offset).template cast<RemoveAutoSizeSpan<ArgType>, std::dynamic_extent, true>());
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) { } else if constexpr (IsPad<ArgType>::value) {
offset += ArgType::Bytes; offset += ArgType::Bytes;
return DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset); return DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset, saveSlots);
} else if constexpr (IsSave<ArgType>::value) {
saveSlots[ArgType::SaveSlot] = buffer.subspan(offset).template as<RemoveSave<ArgType>, true>();
offset += sizeof(RemoveSave<ArgType>);
return DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset, saveSlots);
} else { } else {
if constexpr(sizeof...(ArgTypes) == 0) { if constexpr(sizeof...(ArgTypes) == 0) {
return make_ref_tuple(DecodeArgument<Desc, ArgType>(buffer, offset)); return make_ref_tuple(DecodeArgument<Desc, ArgType>(buffer, offset, saveSlots));
} else { } else {
return std::tuple_cat(make_ref_tuple(DecodeArgument<Desc, ArgType>(buffer, offset)), return std::tuple_cat(make_ref_tuple(DecodeArgument<Desc, ArgType>(buffer, offset, saveSlots)),
DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset)); DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset, saveSlots));
} }
} }
} }
@ -66,6 +81,7 @@ namespace skyline::service::nvdrv::deserialisation {
template<typename Desc, typename... ArgTypes> template<typename Desc, typename... ArgTypes>
constexpr auto DecodeArguments(span<u8, Desc::Size> buffer) { constexpr auto DecodeArguments(span<u8, Desc::Size> buffer) {
size_t offset{}; size_t offset{};
return DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset); std::array<size_t, NumSaveSlots> saveSlots{}; // No need to zero init as used slots will always be loaded first
return DecodeArgumentsImpl<Desc, ArgTypes...>(buffer, offset, saveSlots);
} }
} }

View File

@ -63,7 +63,38 @@ namespace skyline::service::nvdrv::deserialisation {
template<typename T> requires IsOut<T>::value template<typename T> requires IsOut<T>::value
using RemoveOut = typename T::ValueType; using RemoveOut = typename T::ValueType;
// Padding template types
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 {};
// Save to slot template types
constexpr size_t NumSaveSlots{10}; //!< Number of slots to save temporary values in during argument decoding
template<typename T, size_t TSaveSlot> requires (std::is_integral_v<T> && TSaveSlot < NumSaveSlots)
struct Save {
using SaveType = T;
static constexpr auto SaveSlot{TSaveSlot};
};
template<typename>
struct IsSave : std::false_type {};
template<typename T, size_t SaveSlot>
struct IsSave<Save<T, SaveSlot>> : std::true_type {};
template<typename T> requires IsSave<T>::value
using RemoveSave = typename T::SaveType;
// Span template types
template<typename T> requires BufferData<T> template<typename T> requires BufferData<T>
using AutoSizeSpan = span<T>; using AutoSizeSpan = span<T>;
@ -76,17 +107,21 @@ namespace skyline::service::nvdrv::deserialisation {
template<typename T> requires IsAutoSizeSpan<T>::value template<typename T> requires IsAutoSizeSpan<T>::value
using RemoveAutoSizeSpan = typename T::element_type; using RemoveAutoSizeSpan = typename T::element_type;
// Padding template type template<typename T, size_t TSaveSlot> requires (BufferData<T> && TSaveSlot < NumSaveSlots)
template<typename T, size_t Count = 1> requires BufferData<T> struct SlotSizeSpan {
struct Pad { static constexpr auto SaveSlot{TSaveSlot};
static constexpr auto Bytes{Count * sizeof(T)};
using ValueType = T;
}; };
template<typename> template<typename>
struct IsPad : std::false_type {}; struct IsSlotSizeSpan : std::false_type {};
template<typename T, size_t TCount> template<typename T, size_t SaveSlot>
struct IsPad<Pad<T, TCount>> : std::true_type {}; struct IsSlotSizeSpan<SlotSizeSpan<T, SaveSlot>> : std::true_type {};
template<typename T> requires IsSlotSizeSpan<T>::value
using RemoveSlotSizeSpan = typename T::ValueType;
/** /**
* @brief Describes an IOCTL as a type for use in deserialisation * @brief Describes an IOCTL as a type for use in deserialisation