/* * Copyright (c) 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 { 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 && Aligned8Pointer; template concept Aligned32Pointer = AlignedNPointer && Aligned16Pointer; template concept Aligned64Pointer = AlignedNPointer && Aligned32Pointer; template class KUserPointerImplTraits; template requires Aligned8Pointer<_T> class KUserPointerImplTraits<_T> { public: using T = typename std::remove_const::type>::type; public: static ALWAYS_INLINE Result CopyFromUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryFromUser(dst, src, size), svc::ResultInvalidPointer()); R_SUCCEED(); } static ALWAYS_INLINE Result CopyToUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryToUser(dst, src, size), svc::ResultInvalidPointer()); R_SUCCEED(); } }; template requires Aligned32Pointer<_T> class KUserPointerImplTraits<_T> { public: using T = typename std::remove_const::type>::type; public: static ALWAYS_INLINE Result CopyFromUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryFromUserAligned32Bit(dst, src, size), svc::ResultInvalidPointer()); R_SUCCEED(); } static ALWAYS_INLINE Result CopyToUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryToUserAligned32Bit(dst, src, size), svc::ResultInvalidPointer()); R_SUCCEED(); } }; template requires Aligned64Pointer<_T> class KUserPointerImplTraits<_T> { public: using T = typename std::remove_const::type>::type; public: static ALWAYS_INLINE Result CopyFromUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryFromUserAligned64Bit(dst, src, size), svc::ResultInvalidPointer()); R_SUCCEED(); } static ALWAYS_INLINE Result CopyToUserspace(void *dst, const void *src, size_t size) { R_UNLESS(UserspaceAccess::CopyMemoryToUserAligned64Bit(dst, src, size), svc::ResultInvalidPointer()); R_SUCCEED(); } }; template class KUserPointerImpl; template requires Aligned8Pointer<_T> class KUserPointerImpl<_T> : impl::KUserPointerTag { private: using Traits = KUserPointerImplTraits<_T>; protected: using CT = typename std::remove_pointer<_T>::type; using T = typename std::remove_const::type; private: CT *m_ptr; private: ALWAYS_INLINE Result CopyToImpl(void *p, size_t size) const { R_RETURN(Traits::CopyFromUserspace(p, m_ptr, size)); } ALWAYS_INLINE Result CopyFromImpl(const void *p, size_t size) const { R_RETURN(Traits::CopyToUserspace(m_ptr, p, size)); } protected: ALWAYS_INLINE Result CopyTo(T *p) const { R_RETURN(this->CopyToImpl(p, sizeof(*p))); } ALWAYS_INLINE Result CopyFrom(const T *p) const { R_RETURN(this->CopyFromImpl(p, sizeof(*p))); } ALWAYS_INLINE Result CopyArrayElementTo(T *p, size_t index) const { R_RETURN(Traits::CopyFromUserspace(p, m_ptr + index, sizeof(*p))); } ALWAYS_INLINE Result CopyArrayElementFrom(const T *p, size_t index) const { R_RETURN(Traits::CopyToUserspace(m_ptr + index, p, sizeof(*p))); } ALWAYS_INLINE Result CopyArrayTo(T *arr, size_t count) const { R_RETURN(this->CopyToImpl(arr, sizeof(*arr) * count)); } ALWAYS_INLINE Result CopyArrayFrom(const T *arr, size_t count) const { R_RETURN(this->CopyFromImpl(arr, sizeof(*arr) * count)); } constexpr ALWAYS_INLINE bool IsNull() const { return m_ptr == nullptr; } constexpr ALWAYS_INLINE CT *GetUnsafePointer() const { return m_ptr; } }; template<> class KUserPointerImpl : impl::KUserPointerTag { private: using Traits = KUserPointerImplTraits; protected: using CT = const char; using T = char; private: const char *m_ptr; protected: ALWAYS_INLINE Result CopyStringTo(char *dst, size_t size) const { static_assert(sizeof(char) == 1); R_UNLESS(UserspaceAccess::CopyStringFromUser(dst, m_ptr, size) > 0, svc::ResultInvalidPointer()); R_SUCCEED(); } ALWAYS_INLINE Result CopyArrayElementTo(char *dst, size_t index) const { R_RETURN(Traits::CopyFromUserspace(dst, m_ptr + index, sizeof(*dst))); } constexpr ALWAYS_INLINE bool IsNull() const { return m_ptr == nullptr; } constexpr ALWAYS_INLINE const char *GetUnsafePointer() const { return m_ptr; } }; } template struct KUserPointer; template requires impl::ConstPointer struct KUserPointer : 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; using impl::KUserPointerImpl::GetUnsafePointer; }; template requires impl::NonConstPointer struct KUserPointer : 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; using impl::KUserPointerImpl::GetUnsafePointer; }; 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; using impl::KUserPointerImpl::GetUnsafePointer; }; }