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#pragma GCC system_header
20
21#include <l4/sys/cxx/ipc_basics>
22#include <l4/sys/cxx/ipc_iface>
23#include <l4/sys/__typeinfo.h>
24#include <stddef.h>
25
26namespace L4 {
27namespace Ipc {
28namespace Msg {
29namespace Detail {
30
31template<typename T> struct Sizeof { enum { size = sizeof(T) }; };
32template<> struct Sizeof<void> { enum { size = 0 }; };
33
34/**
35 * Argument data structure for server-function arguments.
36 */
37template<typename ...> struct Arg_pack
38{
39  template<typename DIR>
40  unsigned get(char *, unsigned offset, unsigned)
41  { return offset; }
42
43  template<typename DIR>
44  unsigned set(char *, unsigned offset, unsigned, long)
45  { return offset; }
46
47  template<typename F, typename ...ARGS>
48  long call(F f, ARGS ...args)
49  { return f(args...); }
50
51  template<typename O, typename FUNC, typename ...ARGS>
52  long obj_call(O *o, ARGS ...args)
53  {
54    typedef typename FUNC::template fwd<O> Fwd;
55    return Fwd(o).template call<ARGS...>(args...);
56    //return o->op_dispatch(args...);
57  }
58};
59
60/**
61 * Data member for server-function argument T.
62 */
63template<typename T, typename SVR_TYPE, typename ...M>
64struct Svr_arg : Svr_xmit<T>, Arg_pack<M...>
65{
66  typedef Arg_pack<M...> Base;
67
68  typedef SVR_TYPE svr_type;
69  typedef typename _Elem<T>::svr_arg_type svr_arg_type;
70
71  svr_type v;
72
73  template<typename DIR>
74  int get(char *msg, unsigned offset, unsigned limit)
75  {
76    typedef Svr_xmit<T> ct;
77    int r = ct::to_svr(msg, offset, limit, this->v,
78                       typename DIR::dir(), typename DIR::cls());
79    if (L4_LIKELY(r >= 0))
80      return Base::template get<DIR>(msg, r, limit);
81
82    if (_Elem<T>::Is_optional)
83      {
84        v = svr_type();
85        return Base::template get<DIR>(msg, offset, limit);
86      }
87    return r;
88  }
89
90  template<typename DIR>
91  int set(char *msg, unsigned offset, unsigned limit, long ret)
92  {
93    typedef Svr_xmit<T> ct;
94    int r = ct::from_svr(msg, offset, limit, ret, this->v,
95                         typename DIR::dir(), typename DIR::cls());
96    if (L4_UNLIKELY(r < 0))
97      return r;
98    return Base::template set<DIR>(msg, r, limit, ret);
99  }
100
101  template<typename F, typename ...ARGS>
102  long call(F f, ARGS ...args)
103  {
104    //As_arg<value_type> check;
105    return Base::template
106      call<F, ARGS..., svr_arg_type>(f, args..., this->v);
107  }
108
109  template<typename O, typename FUNC, typename ...ARGS>
110  long obj_call(O *o, ARGS ...args)
111  {
112    //As_arg<value_type> check;
113    return Base::template
114      obj_call<O,FUNC,  ARGS..., svr_arg_type>(o, args..., this->v);
115  }
116};
117
118template<typename T, typename ...M>
119struct Svr_arg<T, void, M...> : Arg_pack<M...>
120{
121  typedef Arg_pack<M...> Base;
122
123  template<typename DIR>
124  int get(char *msg, unsigned offset, unsigned limit)
125  { return Base::template get<DIR>(msg, offset, limit); }
126
127  template<typename DIR>
128  int set(char *msg, unsigned offset, unsigned limit, long ret)
129  { return Base::template set<DIR>(msg, offset, limit, ret); }
130
131  template<typename F, typename ...ARGS>
132  long call(F f, ARGS ...args)
133  {
134    return Base::template call<F, ARGS...>(f, args...);
135  }
136
137  template<typename O, typename FUNC, typename ...ARGS>
138  long obj_call(O *o, ARGS ...args)
139  {
140    return Base::template obj_call<O, FUNC, ARGS...>(o, args...);
141  }
142};
143
144template<typename A, typename ...M>
145struct Arg_pack<A, M...> : Svr_arg<A, typename _Elem<A>::svr_type, M...>
146{};
147
148} // namespace Detail
149
150//---------------------------------------------------------------------
151/**
152 * Server-side RPC arguments data structure used to provide arguments
153 * to the server-side implementation of an RPC function.
154 */
155template<typename IPC_TYPE> struct Svr_arg_pack;
156
157template<typename R, typename ...ARGS>
158struct Svr_arg_pack<R (ARGS...)> : Detail::Arg_pack<ARGS...>
159{
160  typedef Detail::Arg_pack<ARGS...> Base;
161  template<typename DIR>
162  int get(void *msg, unsigned offset, unsigned limit)
163  {
164    return Base::template get<DIR>((char *)msg, offset, limit);
165  }
166
167  template<typename DIR>
168  int set(void *msg, unsigned offset, unsigned limit, long ret)
169  {
170    return Base::template set<DIR>((char *)msg, offset, limit, ret);
171  }
172};
173
174/**
175 * Handle an incoming RPC call and forward it to o->op_dispatch().
176 */
177template<typename IPC_TYPE, typename O, typename ...ARGS>
178static l4_msgtag_t
179handle_svr_obj_call(O *o, l4_utcb_t *utcb, l4_msgtag_t tag, ARGS ...args)
180{
181  typedef Svr_arg_pack<typename IPC_TYPE::rpc::ipc_type> Pack;
182  enum
183  {
184    Do_reply  = IPC_TYPE::rpc::flags_type::Is_call,
185    Short_err = Do_reply ? -L4_EMSGTOOSHORT : -L4_ENOREPLY,
186  };
187
188  // XXX: send a reply or just do not reply in case of a cheating client
189  if (L4_UNLIKELY(tag.words() + tag.items() * Item_words > Mr_words))
190    return l4_msgtag(Short_err, 0, 0, 0);
191
192  // our whole arguments data structure
193  Pack pack;
194  l4_msg_regs_t *mrs = l4_utcb_mr_u(utcb);
195
196  int in_pos = Detail::Sizeof<typename IPC_TYPE::opcode_type>::size;
197
198  unsigned const in_bytes = tag.words() * Word_bytes;
199
200  in_pos = pack.template get<Do_in_data>(&mrs->mr[0], in_pos, in_bytes);
201
202  if (L4_UNLIKELY(in_pos < 0))
203    return l4_msgtag(Short_err, 0, 0, 0);
204
205  if (L4_UNLIKELY(pack.template get<Do_out_data>(mrs->mr, 0, Mr_bytes) < 0))
206    return l4_msgtag(Short_err, 0, 0, 0);
207
208
209  in_pos = pack.template get<Do_in_items>(&mrs->mr[tag.words()], 0,
210                                          tag.items() * Item_bytes);
211
212  if (L4_UNLIKELY(in_pos < 0))
213    return l4_msgtag(Short_err, 0, 0, 0);
214
215  asm volatile ("" : "=m" (mrs->mr));
216
217  // call the server function
218  long ret = pack.template obj_call<O, typename IPC_TYPE::rpc, ARGS...>(o, args...);
219
220  if (!Do_reply)
221    return l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
222
223  // our convention says that negative return value means no
224  // reply data
225  if (L4_UNLIKELY(ret < 0))
226    return l4_msgtag(ret, 0, 0, 0);
227
228  // reply with the reply data from the server function
229  int bytes = pack.template set<Do_out_data>(mrs->mr, 0, Mr_bytes, ret);
230  if (L4_UNLIKELY(bytes < 0))
231    return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
232
233  unsigned words = (bytes + Word_bytes - 1) / Word_bytes;
234  bytes = pack.template set<Do_out_items>(&mrs->mr[words], 0,
235                                          Mr_bytes - words * Word_bytes,
236                                          ret);
237  if (L4_UNLIKELY(bytes < 0))
238    return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
239
240  unsigned const items = bytes / Item_bytes;
241  return l4_msgtag(ret, words, items, 0);
242}
243
244//-------------------------------------------------------------------------
245
246template<typename RPCS, typename OPCODE_TYPE>
247struct Dispatch_call;
248
249template<typename CLASS>
250struct Dispatch_call<L4::Typeid::Raw_ipc<CLASS>, void>
251{
252  template<typename OBJ, typename ...ARGS>
253  static l4_msgtag_t
254  call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, ARGS ...a)
255  {
256    return o->op_dispatch(utcb, tag, a...);
257  }
258};
259
260template<typename RPCS>
261struct Dispatch_call<RPCS, void>
262{
263  constexpr static unsigned rmask()
264  { return RPCS::rpc::flags_type::Rights & 3UL; }
265
266  template<typename OBJ, typename ...ARGS>
267  static l4_msgtag_t
268  call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a)
269  {
270    if ((rights & rmask()) != rmask())
271      return l4_msgtag(-L4_EPERM, 0, 0, 0);
272
273    typedef L4::Typeid::Rights<typename RPCS::rpc::class_type> Rights;
274    return handle_svr_obj_call<RPCS>(o, utcb, tag,
275                                     Rights(rights), a...);
276
277  }
278};
279
280template<typename RPCS, typename OPCODE_TYPE>
281struct Dispatch_call
282{
283  constexpr static unsigned rmask()
284  { return RPCS::rpc::flags_type::Rights & 3UL; }
285
286  template<typename OBJ, typename ...ARGS>
287  static l4_msgtag_t
288  _call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, OPCODE_TYPE op, ARGS ...a)
289  {
290    if (L4::Types::Same<typename RPCS::opcode_type, void>::value
291        || RPCS::Opcode == op)
292      {
293        if ((rights & rmask()) != rmask())
294          return l4_msgtag(-L4_EPERM, 0, 0, 0);
295
296        typedef L4::Typeid::Rights<typename RPCS::rpc::class_type> Rights;
297        return handle_svr_obj_call<RPCS>(o, utcb, tag,
298                                         Rights(rights), a...);
299      }
300    return Dispatch_call<typename RPCS::next, OPCODE_TYPE>::template
301      _call<OBJ, ARGS...>(o, utcb, tag, rights, op, a...);
302  }
303
304  template<typename OBJ, typename ...ARGS>
305  static l4_msgtag_t
306  call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a)
307  {
308    OPCODE_TYPE op;
309    unsigned limit = tag.words() * Word_bytes;
310    typedef Svr_xmit<OPCODE_TYPE> S;
311    int err = S::to_svr((char *)l4_utcb_mr_u(utcb)->mr, 0, limit, op,
312                        Dir_in(), Cls_data());
313    if (L4_UNLIKELY(err < 0))
314      return l4_msgtag(-L4_EMSGTOOSHORT, 0, 0, 0);
315
316    return _call<OBJ, ARGS...>(o, utcb, tag, rights, op, a...);
317  }
318};
319
320template<>
321struct Dispatch_call<Typeid::Detail::Rpcs_end, void>
322{
323  template<typename OBJ, typename ...ARGS>
324  static l4_msgtag_t
325  _call(OBJ *, l4_utcb_t *, l4_msgtag_t, unsigned, int, ARGS ...)
326  { return l4_msgtag(-L4_ENOSYS, 0, 0, 0); }
327
328  template<typename OBJ, typename ...ARGS>
329  static l4_msgtag_t
330  call(OBJ *, l4_utcb_t *, l4_msgtag_t, unsigned, ARGS ...)
331  { return l4_msgtag(-L4_ENOSYS, 0, 0, 0); }
332};
333
334template<typename OPCODE_TYPE>
335struct Dispatch_call<Typeid::Detail::Rpcs_end, OPCODE_TYPE> :
336  Dispatch_call<Typeid::Detail::Rpcs_end, void> {};
337
338template<typename RPCS, typename OBJ, typename ...ARGS>
339static l4_msgtag_t
340dispatch_call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a)
341{
342  return Dispatch_call<typename RPCS::type, typename RPCS::opcode_type>::template
343    call<OBJ, ARGS...>(o, utcb, tag, rights, a...);
344}
345
346} // namespace Msg
347} // namesapce Ipc
348} // namespace L4
349
350
351