// vi:set ft=cpp: -*- Mode: C++ -*- /* * (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com> * * This file is part of TUD:OS and distributed under the terms of the * GNU General Public License 2. * Please see the COPYING-GPL-2 file for details. * * As a special exception, you may use this file as part of a free software * library without restriction. Specifically, if other files instantiate * templates or use macros or inline functions from this file, or you compile * this file and link it with other files to produce an executable, this * file does not by itself cause the resulting executable to be covered by * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. */ #pragma once #include "types" #include <l4/sys/utcb.h> #include <l4/sys/err.h> namespace L4 { /// IPC related functionality namespace Ipc { /// IPC Message related functionality namespace Msg { using L4::Types::True; using L4::Types::False; /** * Pad bytes to the given alignment \a align (in bytes) * \param bytes The input value in bytes * \param align The alignment value in bytes * \return the result after padding \a bytes to \a align. */ constexpr unsigned long align_to(unsigned long bytes, unsigned long align) noexcept { return (bytes + align - 1) & ~(align - 1); } /** * Pad \a bytes to the alignment of the type \a T. * \tparam T The data type used for the alignment * \param bytes The value to add the padding to * \return \a bytes padded to achieve the alignment of \a T. */ template<typename T> constexpr unsigned long align_to(unsigned long bytes) noexcept { return align_to(bytes, __alignof(T)); } /** * Check if there is enough space for T from offset to limit. * \tparam T The data type that shall be fitted at \a offset * \param offset The current offset in bytes (must already be padded * if desired). * \param limit The limit in bytes that must not be exceeded after adding * the size of \a T. * \return true if the limit will not be exceeded, false else. */ template<typename T> constexpr bool check_size(unsigned offset, unsigned limit) noexcept { return offset + sizeof(T) <= limit; } /** * Check if there is enough space for an array of T from offset to limit. * \tparam T The data type that shall be fitted at \a offset * \tparam CTYPE Type of the \a cnt parameter * \param offset The current offset in bytes (must already be padded * if desired). * \param limit The limit in bytes that must not be exceeded after adding * \a cnt times the size of \a T. * \param cnt The number of elements of type \a T that shall be put * at \a offset. * \return true if the limit will not be exceeded, false else. */ template<typename T, typename CTYPE> inline bool check_size(unsigned offset, unsigned limit, CTYPE cnt) noexcept { if (L4_UNLIKELY(sizeof(CTYPE) <= sizeof(unsigned) && ~0U / sizeof(T) <= static_cast<unsigned>(cnt))) return false; if (L4_UNLIKELY(sizeof(CTYPE) > sizeof(unsigned) && static_cast<CTYPE>(~0U / sizeof(T)) <= cnt)) return false; return sizeof(T) * cnt <= limit - offset; } enum { /// number of bytes for one message word Word_bytes = sizeof(l4_umword_t), /// number of message words for one message item Item_words = 2, /// number of bytes for one message item Item_bytes = Word_bytes * Item_words, /// number of message words available in the UTCB Mr_words = L4_UTCB_GENERIC_DATA_SIZE, /// number of bytes available in the UTCB message registers Mr_bytes = Word_bytes * Mr_words, /// number of bytes available in the UTCB buffer registers Br_bytes = Word_bytes * L4_UTCB_GENERIC_BUFFERS_SIZE, }; /** * Add some data to a message at offs. * \tparam T The type of the data to add * \param msg pointer to the start of the message * \param offs The current offset within the message, this shall be padded to * the alignment of \a T if \a v is added. * \param limit The size limit in bytes that offset must not exceed. * \param v The value to add to the message * \return The new offset when successful, a negative value if the given * limit will be exceeded. */ template<typename T> inline int msg_add(char *msg, unsigned offs, unsigned limit, T v) noexcept { offs = align_to<T>(offs); if (L4_UNLIKELY(!check_size<T>(offs, limit))) return -L4_EMSGTOOLONG; *reinterpret_cast<typename L4::Types::Remove_const<T>::type *>(msg + offs) = v; return offs + sizeof(T); } /** * Get some data from a message at offs. * \tparam T The type of the data to read * \param msg Pointer to the start of the message * \param offs The current offset within the message, this shall be padded to * the alignment of \a T if a \a v can be read. * \param limit The size limit in bytes that offset must not exceed. * \param v A reference to receive the value from the message * \return The new offset when successful, a negative value if the given * limit will be exceeded. */ template<typename T> inline int msg_get(char *msg, unsigned offs, unsigned limit, T &v) noexcept { offs = align_to<T>(offs); if (L4_UNLIKELY(!check_size<T>(offs, limit))) return -L4_EMSGTOOSHORT; v = *reinterpret_cast<T *>(msg + offs); return offs + sizeof(T); } /// Marker type for input values struct Dir_in { typedef Dir_in type; typedef Dir_in dir; }; /// Marker type for output values struct Dir_out { typedef Dir_out type; typedef Dir_out dir; }; /// Marker type for data values struct Cls_data { typedef Cls_data type; typedef Cls_data cls; }; /// Marker type for item values struct Cls_item { typedef Cls_item type; typedef Cls_item cls; }; /// Marker type for receive buffer values struct Cls_buffer { typedef Cls_buffer type; typedef Cls_buffer cls; }; // Typical combinations /// Marker for Input data struct Do_in_data : Dir_in, Cls_data {}; /// Marker for Output data struct Do_out_data : Dir_out, Cls_data {}; /// Marker for Input items struct Do_in_items : Dir_in, Cls_item {}; /// Marker for Output items struct Do_out_items : Dir_out, Cls_item {}; /// Marker for receive buffers struct Do_rcv_buffers : Dir_in, Cls_buffer {}; // implementation details namespace Detail { template<typename T> struct _Plain { typedef T type; static T deref(T v) noexcept { return v; } }; template<typename T> struct _Plain<T *> { typedef T type; static T &deref(T *v) noexcept { return *v; } }; template<typename T> struct _Plain<T &> { typedef T type; static T &deref(T &v) noexcept { return v; } }; template<typename T> struct _Plain<T const &> { typedef T type; static T const &deref(T const &v) noexcept { return v; } }; template<typename T> struct _Plain<T const *> { typedef T type; static T const &deref(T const *v) noexcept { return *v; } }; } /** * Defines client-side handling of `MTYPE' as RPC argument. * \tparam MTYPE Elem<T>::arg_type (where T is the type used in the RPC * definition) * \tparam DIR Dir_in (client -> server), or Dir_out (server -> client) * \tparam CLASS Cls_data, Cls_item, or Cls_buffer */ template<typename MTYPE, typename DIR, typename CLASS> struct Clnt_val_ops; template<typename T> struct Clnt_noops { template<typename A, typename B> static constexpr int to_msg(char *, unsigned offset, unsigned, T, A, B) noexcept { return offset; } /// copy data from the message to the client reference template<typename A, typename B> static constexpr int from_msg(char *, unsigned offset, unsigned, long, T const &, A, B) noexcept { return offset; } }; template<typename T> struct Svr_noops { template<typename A, typename B> static constexpr int from_svr(char *, unsigned offset, unsigned, long, T, A, B) noexcept { return offset; } /// copy data from the message to the client reference template<typename A, typename B> static constexpr int to_svr(char *, unsigned offset, unsigned, T, A, B) noexcept { return offset; } }; template<typename MTYPE, typename CLASS> struct Clnt_val_ops<MTYPE, Dir_in, CLASS> : Clnt_noops<MTYPE> { using Clnt_noops<MTYPE>::to_msg; /// Copy a T into the message static int to_msg(char *msg, unsigned offset, unsigned limit, MTYPE arg, Dir_in, CLASS) noexcept { return msg_add<MTYPE>(msg, offset, limit, arg); } }; template<typename MTYPE, typename CLASS> struct Clnt_val_ops<MTYPE, Dir_out, CLASS> : Clnt_noops<MTYPE> { using Clnt_noops<MTYPE>::from_msg; /// copy data from the message to the client reference static int from_msg(char *msg, unsigned offset, unsigned limit, long, MTYPE &arg, Dir_out, CLASS) noexcept { return msg_get<MTYPE>(msg, offset, limit, arg); } }; /** * Defines server-side handling for `MTYPE` server arguments. * \tparam MTYPE Elem<T>::svr_type (where T is the type used in the RPC * definition) * \tparam DIR Dir_in (client -> server), or Dir_out (server -> client) * \tparam CLASS Cls_data, Cls_item, or Cls_buffer */ template<typename MTYPE, typename DIR, typename CLASS> struct Svr_val_ops; template<typename MTYPE, typename CLASS> struct Svr_val_ops<MTYPE, Dir_in, CLASS> : Svr_noops<MTYPE> { using Svr_noops<MTYPE>::to_svr; /// copy data from the message to the client reference static int to_svr(char *msg, unsigned offset, unsigned limit, MTYPE &arg, Dir_in, CLASS) noexcept { return msg_get<MTYPE>(msg, offset, limit, arg); } }; template<typename MTYPE, typename CLASS> struct Svr_val_ops<MTYPE, Dir_out, CLASS> : Svr_noops<MTYPE> { using Svr_noops<MTYPE>::to_svr; static int to_svr(char *, unsigned offs, unsigned limit, MTYPE &, Dir_out, CLASS) noexcept { offs = align_to<MTYPE>(offs); if (L4_UNLIKELY(!check_size<MTYPE>(offs, limit))) return -L4_EMSGTOOLONG; return offs + sizeof(MTYPE); } using Svr_noops<MTYPE>::from_svr; /// Copy a T into the message static int from_svr(char *msg, unsigned offset, unsigned limit, long, MTYPE arg, Dir_out, CLASS) noexcept { return msg_add<MTYPE>(msg, offset, limit, arg); } }; template<typename T> struct Elem { /// The type used in client argument list typedef T arg_type; /// The data type used to store the T on the server-side typedef T svr_type; /// The argument type for the server-side function typedef T svr_arg_type; // might by const & (depending on the size) enum { Is_optional = false }; }; template<typename T> struct Elem<T &> { typedef T &arg_type; typedef T svr_type; ///< pass a reference to the server-function too typedef T &svr_arg_type; enum { Is_optional = false }; }; template<typename T> struct Elem<T const &> { typedef T const &arg_type; typedef T svr_type; // as the RPC uses a const reference we use it here too, // we could also use pass by value depending on the size typedef T const &svr_arg_type; enum { Is_optional = false }; }; template<typename T> struct Elem<T *> : Elem<T &> { typedef T *arg_type; }; template<typename T> struct Elem<T const *> : Elem<T const &> { typedef T const *arg_type; }; /// Type trait defining a valid RPC parameter type. template<typename T> struct Is_valid_rpc_type : L4::Types::True {}; // Static assertions outside functions work only properly from C++11 // onewards. On earlier version make sure the compiler fails on an ugly // undefined struct instead. template<typename T, bool B> struct Error_invalid_rpc_parameter_used; template<typename T> struct Error_invalid_rpc_parameter_used<T, true> {}; #if __cplusplus >= 201103L template<typename T> struct _Elem : Elem<T> { static_assert(Is_valid_rpc_type<T>::value, "L4::Ipc::Msg::_Elem<T>: type T is not a valid RPC parameter type."); }; #else template<typename T> struct _Elem : Elem<T>, Error_invalid_rpc_parameter_used<T, Is_valid_rpc_type<T>::value> {}; #endif template<typename T> struct Class : Cls_data {}; template<typename T> struct Direction : Dir_in {}; template<typename T> struct Direction<T const &> : Dir_in {}; template<typename T> struct Direction<T const *> : Dir_in {}; template<typename T> struct Direction<T &> : Dir_out {}; template<typename T> struct Direction<T *> : Dir_out {}; template<typename T> struct _Clnt_noops : Clnt_noops<typename Detail::_Plain<typename _Elem<T>::arg_type>::type> {}; namespace Detail { template<typename T, typename DIR, typename CLASS> struct _Clnt_val_ops : Clnt_val_ops<typename Detail::_Plain<T>::type, DIR, CLASS> {}; template<typename T, typename ELEM = _Elem<T>, typename CLNT_OPS = _Clnt_val_ops<typename ELEM::arg_type, typename Direction<T>::type, typename Class<typename Detail::_Plain<T>::type>::type> > struct _Clnt_xmit : CLNT_OPS {}; template<typename T, typename ELEM = _Elem<T>, typename SVR_OPS = Svr_val_ops<typename ELEM::svr_type, typename Direction<T>::type, typename Class<typename Detail::_Plain<T>::type>::type> > struct _Svr_xmit : SVR_OPS {}; } //namespace Detail template<typename T> struct Clnt_xmit : Detail::_Clnt_xmit<T> {}; template<typename T> struct Svr_xmit : Detail::_Svr_xmit<T> {}; }}} // namespace Msg, Ipc, L4