/* * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include namespace ams::kern::svc { namespace impl { /* TODO: C++20 template concept Pointer = std::is_pointer::value; template concept NonConstPointer = Pointer && !std::is_const::type>::value; template concept ConstPointer = Pointer && std::is_const::type>::value; template concept AlignedNPointer = Pointer && alignof(typename std::remove_pointer::type) >= N && util::IsAligned(sizeof(typename std::remove_pointer::type), N); template concept Aligned8Pointer = AlignedNPointer; template concept Aligned16Pointer = AlignedNPointer && Aligned8; template concept Aligned32Pointer = AlignedNPointer && Aligned16; template concept Aligned64Pointer = AlignedNPointer && Aligned32; */ template constexpr inline bool IsPointer = std::is_pointer::value; template constexpr inline bool IsConstPointer = IsPointer && std::is_const::type>::value; template constexpr inline bool IsNonConstPointer = IsPointer && !std::is_const::type>::value; template constexpr inline bool IsAlignedNPointer = IsPointer && alignof(typename std::remove_pointer::type) >= N && util::IsAligned(sizeof(typename std::remove_pointer::type), N); template /* requires Aligned8Pointer<_T> */ class KUserPointerImplTraits { static_assert(IsAlignedNPointer<_T, sizeof(u8)>); public: using T = typename std::remove_const::type>::type; public: static Result CopyFromUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryFromUser(dst, src, size), svc::ResultInvalidPointer()); return ResultSuccess(); } static Result CopyToUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryToUser(dst, src, size), svc::ResultInvalidPointer()); return ResultSuccess(); } }; template /* requires Aligned32Pointer<_T> */ class KUserPointerImplTraits<_T, typename std::enable_if && !IsAlignedNPointer<_T, sizeof(u64)>>::type> { static_assert(IsAlignedNPointer<_T, sizeof(u32)>); public: using T = typename std::remove_const::type>::type; public: static Result CopyFromUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryFromUserAligned32Bit(dst, src, size), svc::ResultInvalidPointer()); return ResultSuccess(); } static Result CopyToUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryToUserAligned32Bit(dst, src, size), svc::ResultInvalidPointer()); return ResultSuccess(); } }; template /* requires Aligned64Pointer<_T> */ class KUserPointerImplTraits<_T, typename std::enable_if>::type> { static_assert(IsAlignedNPointer<_T, sizeof(u64)>); public: using T = typename std::remove_const::type>::type; public: static Result CopyFromUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryFromUserAligned64Bit(dst, src, size), svc::ResultInvalidPointer()); return ResultSuccess(); } static Result CopyToUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryToUserAligned64Bit(dst, src, size), svc::ResultInvalidPointer()); return ResultSuccess(); } }; template /* requires Aligned8Pointer<_T> */ class KUserPointerImpl : impl::KUserPointerTag { private: using Traits = KUserPointerImplTraits<_T>; protected: using T = typename std::remove_const::type>::type; private: _T *ptr; private: Result CopyToImpl(void *p, size_t size) const { return Traits::CopyFromUserspace(p, this->ptr, size); } Result CopyFromImpl(const void *p, size_t size) const { return Traits::CopyToUserspace(this->ptr, p, size); } protected: Result CopyTo(T *p) const { return this->CopyToImpl(p, sizeof(*p)); } Result CopyFrom(const T *p) const { return this->CopyFromImpl(p, sizeof(*p)); } Result CopyArrayElementTo(T *p, size_t index) const { return Traits::CopyFromUserspace(p, this->ptr + index, sizeof(*p)); } Result CopyArrayElementFrom(const T *p, size_t index) const { return Traits::CopyToUserspace(this->ptr + index, p, sizeof(*p)); } Result CopyArrayTo(T *arr, size_t count) const { return this->CopyToImpl(arr, sizeof(*arr) * count); } Result CopyArrayFrom(const T *arr, size_t count) const { return this->CopyFromImpl(arr, sizeof(*arr) * count); } constexpr bool IsNull() const { return this->ptr == nullptr; } }; template<> class KUserPointerImpl : impl::KUserPointerTag { private: using Traits = KUserPointerImplTraits; protected: using T = char; private: const char *ptr; protected: Result CopyStringTo(char *dst, size_t size) const { static_assert(sizeof(char) == 1); R_UNLESS(UserspaceAccess::CopyStringFromUser(dst, this->ptr, size) > 0, svc::ResultInvalidPointer()); return ResultSuccess(); } Result CopyArrayElementTo(char *dst, size_t index) const { return Traits::CopyFromUserspace(dst, this->ptr + index, sizeof(*dst)); } constexpr bool IsNull() const { return this->ptr == nullptr; } }; } template class KUserPointer; template /* requires impl::ConstPointer */ struct KUserPointer>::type> : public impl::KUserPointerImpl { public: static constexpr bool IsInput = true; public: using impl::KUserPointerImpl::CopyTo; using impl::KUserPointerImpl::CopyArrayElementTo; using impl::KUserPointerImpl::CopyArrayTo; using impl::KUserPointerImpl::IsNull; }; template /* requires impl::NonConstPointer */ struct KUserPointer>::type> : public impl::KUserPointerImpl { public: static constexpr bool IsInput = false; public: using impl::KUserPointerImpl::CopyFrom; using impl::KUserPointerImpl::CopyArrayElementFrom; using impl::KUserPointerImpl::CopyArrayFrom; using impl::KUserPointerImpl::IsNull; }; template<> struct KUserPointer : public impl::KUserPointerImpl { public: static constexpr bool IsInput = true; public: using impl::KUserPointerImpl::CopyStringTo; using impl::KUserPointerImpl::CopyArrayElementTo; using impl::KUserPointerImpl::IsNull; }; }