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