blob: e2421a97070ab2267b713fe3978e9616c41d3986 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#pragma once
#include <string.h>
#include <mxtl/type_support.h>
#include <magenta/types.h>
namespace internal {
template <typename T> inline constexpr size_t type_size() { return sizeof(T); }
template <> inline constexpr size_t type_size<void>() { return 1u; }
template <> inline constexpr size_t type_size<const void>() { return 1u; }
template <> inline constexpr size_t type_size<volatile void>() { return 1u; }
template <> inline constexpr size_t type_size<const volatile void>() { return 1u; }
}
template <typename T>
class user_ptr {
private:
static mx_status_t copy_to_user_unsafe(void *dst, const void* src, size_t size) {
memcpy(dst, src, size);
return MX_OK;
}
static mx_status_t copy_from_user_unsafe(void *dst, const void* src, size_t size) {
memcpy(dst, src, size);
return MX_OK;
}
public:
explicit user_ptr(T* p) : ptr_(p) {}
T* get() const { return ptr_; }
template <typename C>
user_ptr<C> reinterpret() const { return user_ptr<C>(reinterpret_cast<C*>(ptr_)); }
// special operator to return the nullness of the pointer
explicit operator bool() const { return ptr_ != nullptr; }
// Returns a user_ptr pointing to the |index|-th element from this one, or a null user_ptr if
// this pointer is null. Note: This does no other validation, and the behavior is undefined on
// overflow. (Using this will fail to compile if T is |void|.)
user_ptr element_offset(size_t index) const {
return ptr_ ? user_ptr(ptr_ + index) : user_ptr(nullptr);
}
// Returns a user_ptr offset by |offset| bytes from this one.
user_ptr byte_offset(size_t offset) const {
return ptr_ ? user_ptr(reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(ptr_) + offset))
: user_ptr(nullptr);
}
// Copies a single T to user memory. (Using this will fail to compile if T is |void|.)
// Note: The templatization is simply to allow the class to compile if T is |void|.
template <typename S = T>
mx_status_t copy_to_user(const S& src) const {
static_assert(mxtl::is_same<S, T>::value, "Do not use the template parameter.");
return copy_to_user_unsafe(ptr_, &src, sizeof(S));
}
// Copies an array of T to user memory. Note: This takes a count not a size, unless T is |void|.
// WARNING: This does not check that |count| is reasonable (i.e., that multiplication won't
// overflow).
mx_status_t copy_array_to_user(const T* src, size_t count) const {
return copy_to_user_unsafe(ptr_, src, count * internal::type_size<T>());
}
// Copies an array of T to user memory. Note: This takes a count not a size, unless T is |void|.
// WARNING: This does not check that |count| is reasonable (i.e., that multiplication won't
// overflow).
mx_status_t copy_array_to_user(const T* src, size_t count, size_t offset) const {
return copy_to_user_unsafe(ptr_ + offset, src, count * internal::type_size<T>());
}
// Copies a single T from user memory. (Using this will fail to compile if T is |void|.)
mx_status_t copy_from_user(typename mxtl::remove_const<T>::type* dst) const {
// Intentionally use sizeof(T) here, so *using* this method won't compile if T is |void|.
return copy_from_user_unsafe(dst, ptr_, sizeof(T));
}
// Copies an array of T from user memory. Note: This takes a count not a size, unless T is
// |void|.
// WARNING: This does not check that |count| is reasonable (i.e., that multiplication won't
// overflow).
mx_status_t copy_array_from_user(typename mxtl::remove_const<T>::type* dst, size_t count) const {
return copy_from_user_unsafe(dst, ptr_, count * internal::type_size<T>());
}
// Copies a sub-array of T from user memory. Note: This takes a count not a size, unless T is
// |void|.
// WARNING: This does not check that |count| is reasonable (i.e., that multiplication won't
// overflow).
mx_status_t copy_array_from_user(typename mxtl::remove_const<T>::type* dst, size_t count, size_t offset) const {
return copy_from_user_unsafe(dst, ptr_ + offset, count * internal::type_size<T>());
}
private:
// It is very important that this class only wrap the pointer type itself
// and not include any other members so as not to break the ABI between
// the kernel and user space.
T* const ptr_;
};
template <typename T>
user_ptr<T> make_user_ptr(T* p) { return user_ptr<T>(p); }