/* * 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 { 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 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> { 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> { 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 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 *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; } constexpr CT *GetUnsafePointer() const { return this->ptr; } }; template<> class KUserPointerImpl : impl::KUserPointerTag { private: using Traits = KUserPointerImplTraits; protected: using CT = const char; 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; } constexpr const char *GetUnsafePointer() const { return this->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; }; }