L4Re - L4 Runtime Environment
ipc_basics
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 
24 namespace L4 {
25 
26 /// IPC related functionality
27 namespace Ipc {
28 
29 /// IPC Message related functionality
30 namespace Msg {
31 
32 using L4::Types::True;
33 using 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  */
41 inline unsigned long align_to(unsigned long bytes, unsigned long align)
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  */
50 template<typename T>
51 inline unsigned long align_to(unsigned long bytes)
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  */
63 template<typename T>
64 inline bool check_size(unsigned offset, unsigned limit)
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  */
81 template<typename T, typename CTYPE>
82 inline bool check_size(unsigned offset, unsigned limit, CTYPE cnt)
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 
96 enum
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  */
124 template<typename T>
125 inline int msg_add(char *msg, unsigned offs, unsigned limit, T v)
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  */
145 template<typename T>
146 inline int msg_get(char *msg, unsigned offs, unsigned limit, T &v)
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
156 struct Dir_in { typedef Dir_in type; typedef Dir_in dir; };
157 /// Marker type for output values
158 struct Dir_out { typedef Dir_out type; typedef Dir_out dir; };
159 
160 /// Marker type for data values
161 struct Cls_data { typedef Cls_data type; typedef Cls_data cls; };
162 /// Marker type for item values
163 struct Cls_item { typedef Cls_item type; typedef Cls_item cls; };
164 /// Marker type for receive buffer values
165 struct Cls_buffer { typedef Cls_buffer type; typedef Cls_buffer cls; };
166 
167 // Typical combinations
168 /// Marker for Input data
169 struct Do_in_data : Dir_in, Cls_data {};
170 /// Marker for Output data
171 struct Do_out_data : Dir_out, Cls_data {};
172 /// Marker for Input items
173 struct Do_in_items : Dir_in, Cls_item {};
174 /// Marker for Output items
175 struct Do_out_items : Dir_out, Cls_item {};
176 /// Marker for receive buffers
177 struct Do_rcv_buffers : Dir_in, Cls_buffer {};
178 
179 // implementation details
180 namespace Detail {
181 
182 template<typename T> struct _Plain
183 {
184  typedef T type;
185  static T deref(T v) { return v; }
186 };
187 
188 template<typename T> struct _Plain<T *>
189 {
190  typedef T type;
191  static T &deref(T *v) { return *v; }
192 };
193 
194 template<typename T> struct _Plain<T &>
195 {
196  typedef T type;
197  static T &deref(T &v) { return v; }
198 
199 };
200 
201 template<typename T> struct _Plain<T const &>
202 {
203  typedef T type;
204  static T const &deref(T const &v) { return v; }
205 };
206 
207 template<typename T> struct _Plain<T const *>
208 {
209  typedef T type;
210  static T const &deref(T const *v) { 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  */
221 template<typename MTYPE, typename DIR, typename CLASS> struct Clnt_val_ops;
222 
223 template<typename T> struct Clnt_noops
224 {
225  template<typename A, typename B>
226  static int to_msg(char *, unsigned offset, unsigned, T, A, B)
227  { return offset; }
228 
229  /// copy data from the message to the client reference
230  template<typename A, typename B>
231  static int from_msg(char *, unsigned offset, unsigned, long, T const &, A, B)
232  { return offset; }
233 };
234 
235 template<typename T> struct Svr_noops
236 {
237  template<typename A, typename B>
238  static int from_svr(char *, unsigned offset, unsigned, long, T, A, B)
239  { return offset; }
240 
241  /// copy data from the message to the client reference
242  template<typename A, typename B>
243  static int to_svr(char *, unsigned offset, unsigned, T, A, B)
244  { return offset; }
245 };
246 
247 template<typename MTYPE, typename CLASS>
248 struct 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)
254  { return msg_add<MTYPE>(msg, offset, limit, arg); }
255 };
256 
257 
258 template<typename MTYPE, typename CLASS>
259 struct 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)
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  */
275 template<typename MTYPE, typename DIR, typename CLASS> struct Svr_val_ops;
276 
277 template<typename MTYPE, typename CLASS>
278 struct 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)
284  { return msg_get<MTYPE>(msg, offset, limit, arg); }
285 };
286 
287 template<typename MTYPE, typename CLASS>
288 struct 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)
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)
304  { return msg_add<MTYPE>(msg, offset, limit, arg); }
305 };
306 
307 template<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 
320 template<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 
329 template<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 
339 template<typename T> struct Elem<T *> : Elem<T &>
340 {
341  typedef T *arg_type;
342 };
343 
344 template<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.
350 template<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.
355 template<typename T, bool B> struct Error_invalid_rpc_parameter_used;
356 template<typename T> struct Error_invalid_rpc_parameter_used<T, true> {};
357 
358 #if __cplusplus >= 201103L
359 template<typename T>
360 struct _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
366 template<typename T>
367 struct _Elem : Elem<T>,
368  Error_invalid_rpc_parameter_used<T, Is_valid_rpc_type<T>::value>
369 {};
370 #endif
371 
372 
373 template<typename T> struct Class : Cls_data {};
374 template<typename T> struct Direction : Dir_in {};
375 template<typename T> struct Direction<T const &> : Dir_in {};
376 template<typename T> struct Direction<T const *> : Dir_in {};
377 template<typename T> struct Direction<T &> : Dir_out {};
378 template<typename T> struct Direction<T *> : Dir_out {};
379 
380 template<typename T> struct _Clnt_noops :
381  Clnt_noops<typename Detail::_Plain<typename _Elem<T>::arg_type>::type>
382 {};
383 
384 namespace Detail {
385 
386 template<typename T, typename DIR, typename CLASS>
387 struct _Clnt_val_ops :
388  Clnt_val_ops<typename Detail::_Plain<T>::type, DIR, CLASS> {};
389 
390 template<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  >
396 struct _Clnt_xmit : CLNT_OPS {};
397 
398 template<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  >
404 struct _Svr_xmit : SVR_OPS {};
405 
406 } //namespace Detail
407 template<typename T> struct Clnt_xmit : Detail::_Clnt_xmit<T> {};
408 template<typename T> struct Svr_xmit : Detail::_Svr_xmit<T> {};
409 
410 }}} // namespace Msg, Ipc, L4
411 
412