/*
* 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;
};
}