1// vi:set ft=cpp: -*- Mode: C++ -*- 2/* 3 * (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com> 4 * 5 * This file is part of TUD:OS and distributed under the terms of the 6 * GNU General Public License 2. 7 * Please see the COPYING-GPL-2 file for details. 8 * 9 * As a special exception, you may use this file as part of a free software 10 * library without restriction. Specifically, if other files instantiate 11 * templates or use macros or inline functions from this file, or you compile 12 * this file and link it with other files to produce an executable, this 13 * file does not by itself cause the resulting executable to be covered by 14 * the GNU General Public License. This exception does not however 15 * invalidate any other reasons why the executable file might be covered by 16 * the GNU General Public License. 17 */ 18#pragma once 19 20#include "types" 21#include <l4/sys/utcb.h> 22#include <l4/sys/err.h> 23 24namespace L4 { 25 26/// IPC related functionality 27namespace Ipc { 28 29/// IPC Message related functionality 30namespace Msg { 31 32using L4::Types::True; 33using L4::Types::False; 34 35/** 36 * Pad bytes to the given alignment \a align (in bytes) 37 * \param bytes The input value in bytes 38 * \param align The alignment value in bytes 39 * \return the result after padding \a bytes to \a align. 40 */ 41constexpr unsigned long align_to(unsigned long bytes, unsigned long align) noexcept 42{ return (bytes + align - 1) & ~(align - 1); } 43 44/** 45 * Pad \a bytes to the alignment of the type \a T. 46 * \tparam T The data type used for the alignment 47 * \param bytes The value to add the padding to 48 * \return \a bytes padded to achieve the alignment of \a T. 49 */ 50template<typename T> 51constexpr unsigned long align_to(unsigned long bytes) noexcept 52{ return align_to(bytes, __alignof(T)); } 53 54/** 55 * Check if there is enough space for T from offset to limit. 56 * \tparam T The data type that shall be fitted at \a offset 57 * \param offset The current offset in bytes (must already be padded 58 * if desired). 59 * \param limit The limit in bytes that must not be exceeded after adding 60 * the size of \a T. 61 * \return true if the limit will not be exceeded, false else. 62 */ 63template<typename T> 64constexpr bool check_size(unsigned offset, unsigned limit) noexcept 65{ 66 return offset + sizeof(T) <= limit; 67} 68 69/** 70 * Check if there is enough space for an array of T from offset to limit. 71 * \tparam T The data type that shall be fitted at \a offset 72 * \tparam CTYPE Type of the \a cnt parameter 73 * \param offset The current offset in bytes (must already be padded 74 * if desired). 75 * \param limit The limit in bytes that must not be exceeded after adding 76 * \a cnt times the size of \a T. 77 * \param cnt The number of elements of type \a T that shall be put 78 * at \a offset. 79 * \return true if the limit will not be exceeded, false else. 80 */ 81template<typename T, typename CTYPE> 82inline bool check_size(unsigned offset, unsigned limit, CTYPE cnt) noexcept 83{ 84 if (L4_UNLIKELY(sizeof(CTYPE) <= sizeof(unsigned) && 85 ~0U / sizeof(T) <= static_cast<unsigned>(cnt))) 86 return false; 87 88 if (L4_UNLIKELY(sizeof(CTYPE) > sizeof(unsigned) && 89 static_cast<CTYPE>(~0U / sizeof(T)) <= cnt)) 90 return false; 91 92 return sizeof(T) * cnt <= limit - offset; 93} 94 95 96enum 97{ 98 /// number of bytes for one message word 99 Word_bytes = sizeof(l4_umword_t), 100 /// number of message words for one message item 101 Item_words = 2, 102 /// number of bytes for one message item 103 Item_bytes = Word_bytes * Item_words, 104 /// number of message words available in the UTCB 105 Mr_words = L4_UTCB_GENERIC_DATA_SIZE, 106 /// number of bytes available in the UTCB message registers 107 Mr_bytes = Word_bytes * Mr_words, 108 /// number of bytes available in the UTCB buffer registers 109 Br_bytes = Word_bytes * L4_UTCB_GENERIC_BUFFERS_SIZE, 110}; 111 112 113/** 114 * Add some data to a message at offs. 115 * \tparam T The type of the data to add 116 * \param msg pointer to the start of the message 117 * \param offs The current offset within the message, this shall be padded to 118 * the alignment of \a T if \a v is added. 119 * \param limit The size limit in bytes that offset must not exceed. 120 * \param v The value to add to the message 121 * \return The new offset when successful, a negative value if the given 122 * limit will be exceeded. 123 */ 124template<typename T> 125inline int msg_add(char *msg, unsigned offs, unsigned limit, T v) noexcept 126{ 127 offs = align_to<T>(offs); 128 if (L4_UNLIKELY(!check_size<T>(offs, limit))) 129 return -L4_EMSGTOOLONG; 130 *reinterpret_cast<typename L4::Types::Remove_const<T>::type *>(msg + offs) = v; 131 return offs + sizeof(T); 132} 133 134/** 135 * Get some data from a message at offs. 136 * \tparam T The type of the data to read 137 * \param msg Pointer to the start of the message 138 * \param offs The current offset within the message, this shall be padded to 139 * the alignment of \a T if a \a v can be read. 140 * \param limit The size limit in bytes that offset must not exceed. 141 * \param v A reference to receive the value from the message 142 * \return The new offset when successful, a negative value if the given 143 * limit will be exceeded. 144 */ 145template<typename T> 146inline int msg_get(char *msg, unsigned offs, unsigned limit, T &v) noexcept 147{ 148 offs = align_to<T>(offs); 149 if (L4_UNLIKELY(!check_size<T>(offs, limit))) 150 return -L4_EMSGTOOSHORT; 151 v = *reinterpret_cast<T *>(msg + offs); 152 return offs + sizeof(T); 153} 154 155/// Marker type for input values 156struct Dir_in { typedef Dir_in type; typedef Dir_in dir; }; 157/// Marker type for output values 158struct Dir_out { typedef Dir_out type; typedef Dir_out dir; }; 159 160/// Marker type for data values 161struct Cls_data { typedef Cls_data type; typedef Cls_data cls; }; 162/// Marker type for item values 163struct Cls_item { typedef Cls_item type; typedef Cls_item cls; }; 164/// Marker type for receive buffer values 165struct Cls_buffer { typedef Cls_buffer type; typedef Cls_buffer cls; }; 166 167// Typical combinations 168/// Marker for Input data 169struct Do_in_data : Dir_in, Cls_data {}; 170/// Marker for Output data 171struct Do_out_data : Dir_out, Cls_data {}; 172/// Marker for Input items 173struct Do_in_items : Dir_in, Cls_item {}; 174/// Marker for Output items 175struct Do_out_items : Dir_out, Cls_item {}; 176/// Marker for receive buffers 177struct Do_rcv_buffers : Dir_in, Cls_buffer {}; 178 179// implementation details 180namespace Detail { 181 182template<typename T> struct _Plain 183{ 184 typedef T type; 185 static T deref(T v) noexcept { return v; } 186}; 187 188template<typename T> struct _Plain<T *> 189{ 190 typedef T type; 191 static T &deref(T *v) noexcept { return *v; } 192}; 193 194template<typename T> struct _Plain<T &> 195{ 196 typedef T type; 197 static T &deref(T &v) noexcept { return v; } 198 199}; 200 201template<typename T> struct _Plain<T const &> 202{ 203 typedef T type; 204 static T const &deref(T const &v) noexcept { return v; } 205}; 206 207template<typename T> struct _Plain<T const *> 208{ 209 typedef T type; 210 static T const &deref(T const *v) noexcept { return *v; } 211}; 212} 213 214/** 215 * Defines client-side handling of `MTYPE' as RPC argument. 216 * \tparam MTYPE Elem<T>::arg_type (where T is the type used in the RPC 217 * definition) 218 * \tparam DIR Dir_in (client -> server), or Dir_out (server -> client) 219 * \tparam CLASS Cls_data, Cls_item, or Cls_buffer 220 */ 221template<typename MTYPE, typename DIR, typename CLASS> struct Clnt_val_ops; 222 223template<typename T> struct Clnt_noops 224{ 225 template<typename A, typename B> 226 static constexpr int to_msg(char *, unsigned offset, unsigned, T, A, B) noexcept 227 { return offset; } 228 229 /// copy data from the message to the client reference 230 template<typename A, typename B> 231 static constexpr int from_msg(char *, unsigned offset, unsigned, long, T const &, A, B) noexcept 232 { return offset; } 233}; 234 235template<typename T> struct Svr_noops 236{ 237 template<typename A, typename B> 238 static constexpr int from_svr(char *, unsigned offset, unsigned, long, T, A, B) noexcept 239 { return offset; } 240 241 /// copy data from the message to the client reference 242 template<typename A, typename B> 243 static constexpr int to_svr(char *, unsigned offset, unsigned, T, A, B) noexcept 244 { return offset; } 245}; 246 247template<typename MTYPE, typename CLASS> 248struct Clnt_val_ops<MTYPE, Dir_in, CLASS> : Clnt_noops<MTYPE> 249{ 250 using Clnt_noops<MTYPE>::to_msg; 251 /// Copy a T into the message 252 static int to_msg(char *msg, unsigned offset, unsigned limit, 253 MTYPE arg, Dir_in, CLASS) noexcept 254 { return msg_add<MTYPE>(msg, offset, limit, arg); } 255}; 256 257 258template<typename MTYPE, typename CLASS> 259struct Clnt_val_ops<MTYPE, Dir_out, CLASS> : Clnt_noops<MTYPE> 260{ 261 using Clnt_noops<MTYPE>::from_msg; 262 /// copy data from the message to the client reference 263 static int from_msg(char *msg, unsigned offset, unsigned limit, long, 264 MTYPE &arg, Dir_out, CLASS) noexcept 265 { return msg_get<MTYPE>(msg, offset, limit, arg); } 266}; 267 268/** 269 * Defines server-side handling for `MTYPE` server arguments. 270 * \tparam MTYPE Elem<T>::svr_type (where T is the type used in the RPC 271 * definition) 272 * \tparam DIR Dir_in (client -> server), or Dir_out (server -> client) 273 * \tparam CLASS Cls_data, Cls_item, or Cls_buffer 274 */ 275template<typename MTYPE, typename DIR, typename CLASS> struct Svr_val_ops; 276 277template<typename MTYPE, typename CLASS> 278struct Svr_val_ops<MTYPE, Dir_in, CLASS> : Svr_noops<MTYPE> 279{ 280 using Svr_noops<MTYPE>::to_svr; 281 /// copy data from the message to the client reference 282 static int to_svr(char *msg, unsigned offset, unsigned limit, 283 MTYPE &arg, Dir_in, CLASS) noexcept 284 { return msg_get<MTYPE>(msg, offset, limit, arg); } 285}; 286 287template<typename MTYPE, typename CLASS> 288struct Svr_val_ops<MTYPE, Dir_out, CLASS> : Svr_noops<MTYPE> 289{ 290 using Svr_noops<MTYPE>::to_svr; 291 static int to_svr(char *, unsigned offs, unsigned limit, 292 MTYPE &, Dir_out, CLASS) noexcept 293 { 294 offs = align_to<MTYPE>(offs); 295 if (L4_UNLIKELY(!check_size<MTYPE>(offs, limit))) 296 return -L4_EMSGTOOLONG; 297 return offs + sizeof(MTYPE); 298 } 299 300 using Svr_noops<MTYPE>::from_svr; 301 /// Copy a T into the message 302 static int from_svr(char *msg, unsigned offset, unsigned limit, long, 303 MTYPE arg, Dir_out, CLASS) noexcept 304 { return msg_add<MTYPE>(msg, offset, limit, arg); } 305}; 306 307template<typename T> struct Elem 308{ 309 /// The type used in client argument list 310 typedef T arg_type; 311 /// The data type used to store the T on the server-side 312 typedef T svr_type; 313 /// The argument type for the server-side function 314 typedef T svr_arg_type; // might by const & (depending on the size) 315 316 enum { Is_optional = false }; 317 318}; 319 320template<typename T> struct Elem<T &> 321{ 322 typedef T &arg_type; 323 typedef T svr_type; 324 ///< pass a reference to the server-function too 325 typedef T &svr_arg_type; 326 enum { Is_optional = false }; 327}; 328 329template<typename T> struct Elem<T const &> 330{ 331 typedef T const &arg_type; 332 typedef T svr_type; 333 // as the RPC uses a const reference we use it here too, 334 // we could also use pass by value depending on the size 335 typedef T const &svr_arg_type; 336 enum { Is_optional = false }; 337}; 338 339template<typename T> struct Elem<T *> : Elem<T &> 340{ 341 typedef T *arg_type; 342}; 343 344template<typename T> struct Elem<T const *> : Elem<T const &> 345{ 346 typedef T const *arg_type; 347}; 348 349/// Type trait defining a valid RPC parameter type. 350template<typename T> struct Is_valid_rpc_type : L4::Types::True {}; 351 352// Static assertions outside functions work only properly from C++11 353// onewards. On earlier version make sure the compiler fails on an ugly 354// undefined struct instead. 355template<typename T, bool B> struct Error_invalid_rpc_parameter_used; 356template<typename T> struct Error_invalid_rpc_parameter_used<T, true> {}; 357 358#if __cplusplus >= 201103L 359template<typename T> 360struct _Elem : Elem<T> 361{ 362 static_assert(Is_valid_rpc_type<T>::value, 363 "L4::Ipc::Msg::_Elem<T>: type T is not a valid RPC parameter type."); 364}; 365#else 366template<typename T> 367struct _Elem : Elem<T>, 368 Error_invalid_rpc_parameter_used<T, Is_valid_rpc_type<T>::value> 369{}; 370#endif 371 372 373template<typename T> struct Class : Cls_data {}; 374template<typename T> struct Direction : Dir_in {}; 375template<typename T> struct Direction<T const &> : Dir_in {}; 376template<typename T> struct Direction<T const *> : Dir_in {}; 377template<typename T> struct Direction<T &> : Dir_out {}; 378template<typename T> struct Direction<T *> : Dir_out {}; 379 380template<typename T> struct _Clnt_noops : 381 Clnt_noops<typename Detail::_Plain<typename _Elem<T>::arg_type>::type> 382{}; 383 384namespace Detail { 385 386template<typename T, typename DIR, typename CLASS> 387struct _Clnt_val_ops : 388 Clnt_val_ops<typename Detail::_Plain<T>::type, DIR, CLASS> {}; 389 390template<typename T, 391 typename ELEM = _Elem<T>, 392 typename CLNT_OPS = _Clnt_val_ops<typename ELEM::arg_type, 393 typename Direction<T>::type, 394 typename Class<typename Detail::_Plain<T>::type>::type> 395 > 396struct _Clnt_xmit : CLNT_OPS {}; 397 398template<typename T, 399 typename ELEM = _Elem<T>, 400 typename SVR_OPS = Svr_val_ops<typename ELEM::svr_type, 401 typename Direction<T>::type, 402 typename Class<typename Detail::_Plain<T>::type>::type> 403 > 404struct _Svr_xmit : SVR_OPS {}; 405 406} //namespace Detail 407template<typename T> struct Clnt_xmit : Detail::_Clnt_xmit<T> {}; 408template<typename T> struct Svr_xmit : Detail::_Svr_xmit<T> {}; 409 410}}} // namespace Msg, Ipc, L4 411 412 413