L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
ipc_server
1// vi:set ft=cpp: -*- Mode: C++ -*-
2/*
3 * (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com>
4 *
5 * License: see LICENSE.spdx (in this directory or the directories above)
6 */
7#pragma once
8#pragma GCC system_header
9
10#include <l4/sys/cxx/ipc_basics>
11#include <l4/sys/cxx/ipc_iface>
12#include <l4/sys/cxx/limits>
13#include <l4/sys/__typeinfo.h>
14#include <l4/sys/assert.h>
15#include <l4/sys/err.h>
16#include <stddef.h>
17
18namespace L4 {
19namespace Ipc {
20namespace Msg {
21namespace Detail {
22
23template<typename T> struct Sizeof { enum { size = sizeof(T) }; };
24template<> struct Sizeof<void> { enum { size = 0 }; };
25
29template<typename ...> struct Arg_pack
30{
31 template<typename DIR>
32 unsigned get(char *, unsigned offset, unsigned)
33 { return offset; }
34
35 template<typename DIR>
36 unsigned set(char *, unsigned offset, unsigned, l4_ret_t)
37 { return offset; }
38
39 template<typename F, typename ...ARGS>
40 decltype(auto) call(F f, ARGS ...args)
41 { return f(args...); }
42
43 template<typename ...>
44 void reply()
45 {}
46
47 template<typename O, typename FUNC, typename ...ARGS>
48 auto obj_call(O *o, ARGS ...args)
49 -> decltype(typename FUNC::template fwd<O>(o).template call<ARGS...>(args...))
50 {
51 using Fwd = typename FUNC::template fwd<O>;
52 using Idl_ret = typename FUNC::result_type;
53 using Svr_ret = decltype(Fwd(o).template call<ARGS...>(args...));
54 auto ret = Fwd(o).template call<ARGS...>(args...);
55
56 using L4::Types::numeric_limits;
57 // Truncating a negative return value which is too small could result in a
58 // positive value pretending "success" to the caller.
59 if constexpr (numeric_limits<Svr_ret>::min() < numeric_limits<Idl_ret>::min())
60 {
61 if (L4_UNLIKELY(ret < numeric_limits<Idl_ret>::min()))
62 return -L4_EMSGERRRANGE;
63 }
64 // Truncating a positive return value which is too large results either in a
65 // positive value different from the original return value (but interpreted
66 // as "success" by the caller) or in a negative value. Trigger an assertion
67 // in these cases but also return an error in case of NDEBUG=y.
68 if constexpr (numeric_limits<Svr_ret>::max() > numeric_limits<Idl_ret>::max())
69 {
70 l4_assert(ret <= numeric_limits<Idl_ret>::max());
71 if (L4_UNLIKELY(ret > numeric_limits<Idl_ret>::max()))
72 return -L4_EMSGERRRANGE;
73 }
74 // The following two checks are required in case the Idl_ret limits exceed
75 // the limits of the 'label' part of l4_msgtag_t.
76 if constexpr (numeric_limits<Svr_ret>::min() < numeric_limits<l4_msgtag_t>::min())
77 {
78 if (L4_UNLIKELY(ret < numeric_limits<l4_msgtag_t>::min()))
79 return -L4_EMSGERRRANGE;
80 }
81 if constexpr (numeric_limits<Svr_ret>::max() > numeric_limits<l4_msgtag_t>::max())
82 {
83 l4_assert(ret <= numeric_limits<l4_msgtag_t>::max());
84 if (L4_UNLIKELY(ret > numeric_limits<l4_msgtag_t>::max()))
85 return -L4_EMSGERRRANGE;
86 }
87
88 return ret;
89 }
90};
91
95template<typename T, typename SVR_TYPE, typename ...M>
96struct Svr_arg : Svr_xmit<T>, Arg_pack<M...>
97{
98 using Base = Arg_pack<M...>;
99
100 using svr_type = SVR_TYPE;
101 using svr_arg_type = typename _Elem<T>::svr_arg_type;
102
103 svr_type v;
104
105 template<typename DIR>
106 int get(char *msg, unsigned offset, unsigned limit)
107 {
108 using ct = Svr_xmit<T>;
109 int r = ct::to_svr(msg, offset, limit, this->v,
110 typename DIR::dir(), typename DIR::cls());
111 if (L4_LIKELY(r >= 0))
112 return Base::template get<DIR>(msg, r, limit);
113
114 if (_Elem<T>::Is_optional)
115 {
116 v = svr_type();
117 return Base::template get<DIR>(msg, offset, limit);
118 }
119 return r;
120 }
121
122 template<typename DIR>
123 int set(char *msg, unsigned offset, unsigned limit, l4_ret_t ret)
124 {
125 using ct = Svr_xmit<T>;
126 int r = ct::from_svr(msg, offset, limit, ret, this->v,
127 typename DIR::dir(), typename DIR::cls());
128 if (L4_UNLIKELY(r < 0))
129 return r;
130 return Base::template set<DIR>(msg, r, limit, ret);
131 }
132
133 template<typename F, typename ...ARGS>
134 decltype(auto) call(F f, ARGS ...args)
135 {
136 //As_arg<value_type> check;
137 return Base::template
138 call<F, ARGS..., svr_arg_type>(f, args..., this->v);
139 }
140
141 template<typename ...ARGS>
142 void reply(ARGS &&...args)
143 {
144 // Only consume out parameters or parameters of type In_out.
145 if constexpr (L4::Types::Same_v<typename Direction<T>::type, Dir_out>
146 || L4::Types::Same_template_v<T, In_out>)
147 reply_consume_arg<ARGS...>(L4::Types::forward<ARGS>(args)...);
148 else
149 Base::template reply<ARGS...>(L4::Types::forward<ARGS>(args)...);
150 }
151
152 template<typename O, typename FUNC, typename ...ARGS>
153 decltype(auto) obj_call(O *o, ARGS ...args)
154 {
155 //As_arg<value_type> check;
156 return Base::template
157 obj_call<O, FUNC, ARGS..., svr_arg_type>(o, args..., this->v);
158 }
159
160private:
161 template<typename A, typename ...ARGS>
162 void reply_consume_arg(A &&a, ARGS &&...args)
163 {
164 this->v = L4::Types::move(a);
165 Base::template reply<ARGS...>(L4::Types::forward<ARGS>(args)...);
166 }
167};
168
174template<typename T, typename ...M>
175struct Svr_arg<T, void, M...> : Arg_pack<M...>
176{};
177
178template<typename A, typename ...M>
179struct Arg_pack<A, M...> : Svr_arg<A, typename _Elem<A>::svr_type, M...>
180{};
181
182} // namespace Detail
183
184//---------------------------------------------------------------------
189template<typename IPC_TYPE> struct Svr_arg_pack;
190
191template<typename R, typename ...ARGS>
192struct Svr_arg_pack<R (ARGS...)> : Detail::Arg_pack<ARGS...>
193{
194 using Base = Detail::Arg_pack<ARGS...>;
195 template<typename DIR>
196 int get(void *msg, unsigned offset, unsigned limit)
197 {
198 char *buf = static_cast<char *>(msg);
199 return Base::template get<DIR>(buf, offset, limit);
200 }
201
202 template<typename DIR>
203 int set(void *msg, unsigned offset, unsigned limit, l4_ret_t ret)
204 {
205 char *buf = static_cast<char *>(msg);
206 return Base::template set<DIR>(buf, offset, limit, ret);
207 }
208};
209
213template<typename IPC_TYPE, typename O, typename ...ARGS>
214static l4_msgtag_t
215handle_svr_obj_call(O *o, l4_utcb_t *utcb, l4_msgtag_t tag, ARGS ...args)
216{
217 using Pack = Svr_arg_pack<typename IPC_TYPE::rpc::ipc_type>;
218 enum
219 {
220 Do_reply = IPC_TYPE::rpc::flags_type::Is_call,
221 Short_err = Do_reply ? -L4_EMSGTOOSHORT : -L4_ENOREPLY,
222 };
223
224 // XXX: send a reply or just do not reply in case of a cheating client
225 if (L4_UNLIKELY(tag.words() + tag.items() * Item_words > Mr_words))
226 return l4_msgtag(Short_err, 0, 0, 0);
227
228 // our whole arguments data structure
229 Pack pack;
230 l4_msg_regs_t *mrs = l4_utcb_mr_u(utcb);
231
232 int in_pos = Detail::Sizeof<typename IPC_TYPE::opcode_type>::size;
233
234 unsigned const in_bytes = tag.words() * Word_bytes;
235
236 in_pos = pack.template get<Do_in_data>(&mrs->mr[0], in_pos, in_bytes);
237
238 if (L4_UNLIKELY(in_pos < 0))
239 return l4_msgtag(Short_err, 0, 0, 0);
240
241 if (L4_UNLIKELY(pack.template get<Do_out_data>(mrs->mr, 0, Mr_bytes) < 0))
242 return l4_msgtag(Short_err, 0, 0, 0);
243
244
245 in_pos = pack.template get<Do_in_items>(&mrs->mr[tag.words()], 0,
246 tag.items() * Item_bytes);
247
248 if (L4_UNLIKELY(in_pos < 0))
249 return l4_msgtag(Short_err, 0, 0, 0);
250
251 asm volatile ("" : "=m" (mrs->mr));
252
253 // call the server function
254 auto ret = pack.template obj_call<O, typename IPC_TYPE::rpc, ARGS...>(o, args...);
255
256 if (!Do_reply)
257 return l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
258
259 // our convention says that negative return value means no
260 // reply data
261 if (L4_UNLIKELY(ret < 0))
262 return l4_msgtag(ret, 0, 0, 0);
263
264 // reply with the reply data from the server function
265 int bytes = pack.template set<Do_out_data>(mrs->mr, 0, Mr_bytes, ret);
266 if (L4_UNLIKELY(bytes < 0))
267 return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
268
269 unsigned words = (bytes + Word_bytes - 1) / Word_bytes;
270 bytes = pack.template set<Do_out_items>(&mrs->mr[words], 0,
271 Mr_bytes - words * Word_bytes,
272 ret);
273 if (L4_UNLIKELY(bytes < 0))
274 return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
275
276 unsigned const items = bytes / Item_bytes;
277 return l4_msgtag(ret, words, items, 0);
278}
279
280
281template<typename RPC, typename ...OUT_ARGS>
282static l4_msgtag_t
283pack_svr_reply(l4_utcb_t *utcb, l4_ret_t ret, OUT_ARGS && ...args)
284{
285 static_assert(RPC::flags_type::Is_call, "RPC to reply to must be a call.");
286
287 // our convention says that negative return value means no
288 // reply data
289 if (L4_UNLIKELY(ret < 0))
290 return l4_msgtag(ret, 0, 0, 0);
291
292 // TODO: Could try to filter out Dir_in parameters already in the arg pack.
294
295 // our whole arguments data structure
296 Pack pack;
297 l4_msg_regs_t *mrs = l4_utcb_mr_u(utcb);
298
299 // Reserves spaces for all data output arguments.
300 if (L4_UNLIKELY(pack.template get<Do_out_data>(mrs->mr, 0, Mr_bytes) < 0))
301 return l4_msgtag(-L4_EMSGTOOSHORT, 0, 0, 0);
302
303 // call the server reply function
304 pack.template reply<OUT_ARGS...>(L4::Types::forward<OUT_ARGS>(args)...);
305
306 // reply with the reply data from the server function
307 int bytes = pack.template set<Do_out_data>(mrs->mr, 0, Mr_bytes, ret);
308 if (L4_UNLIKELY(bytes < 0))
309 return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
310
311 unsigned words = (bytes + Word_bytes - 1) / Word_bytes;
312 bytes = pack.template set<Do_out_items>(&mrs->mr[words], 0,
313 Mr_bytes - words * Word_bytes,
314 ret);
315 if (L4_UNLIKELY(bytes < 0))
316 return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
317
318 unsigned const items = bytes / Item_bytes;
319 return l4_msgtag(ret, words, items, 0);
320}
321
335template<typename RPC, typename ...OUT_ARGS>
336static l4_ret_t
337reply(L4::Reply_cap reply_cap, l4_ret_t res, OUT_ARGS && ...args)
338{
339 l4_utcb_t *u = l4_utcb();
340 return reply_cap.reply(
341 L4::Ipc::Msg::pack_svr_reply<RPC, OUT_ARGS...>(
342 u, res, L4::Types::forward<OUT_ARGS>(args)...),
343 u);
344}
345
346//-------------------------------------------------------------------------
347
348template<typename RPCS, typename OPCODE_TYPE>
349struct Dispatch_call;
350
351template<typename CLASS>
352struct Dispatch_call<L4::Typeid::Raw_ipc<CLASS>, void>
353{
354 template<typename OBJ, typename ...ARGS>
355 static l4_msgtag_t
356 call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, ARGS ...a)
357 {
358 return o->op_dispatch(utcb, tag, a...);
359 }
360};
361
362template<typename RPCS>
363struct Dispatch_call<RPCS, void>
364{
365 constexpr static unsigned rmask()
366 { return RPCS::rpc::flags_type::Rights & 3UL; }
367
368 template<typename OBJ, typename ...ARGS>
369 static l4_msgtag_t
370 call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a)
371 {
372 if ((rights & rmask()) != rmask())
373 return l4_msgtag(-L4_EPERM, 0, 0, 0);
374
375 using Rights = L4::Typeid::Rights<typename RPCS::rpc::class_type>;
376 return handle_svr_obj_call<RPCS>(o, utcb, tag,
377 Rights(rights), a...);
378
379 }
380};
381
382template<typename RPCS, typename OPCODE_TYPE>
383struct Dispatch_call
384{
385 constexpr static unsigned rmask()
386 { return RPCS::rpc::flags_type::Rights & 3UL; }
387
388 template<typename OBJ, typename ...ARGS>
389 static l4_msgtag_t
390 _call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, OPCODE_TYPE op, ARGS ...a)
391 {
392 if (L4::Types::Same_v<typename RPCS::opcode_type, void>
393 || RPCS::Opcode == op)
394 {
395 if ((rights & rmask()) != rmask())
396 return l4_msgtag(-L4_EPERM, 0, 0, 0);
397
398 using Rights = L4::Typeid::Rights<typename RPCS::rpc::class_type>;
399 return handle_svr_obj_call<RPCS>(o, utcb, tag,
400 Rights(rights), a...);
401 }
402 return Dispatch_call<typename RPCS::next, OPCODE_TYPE>::template
403 _call<OBJ, ARGS...>(o, utcb, tag, rights, op, a...);
404 }
405
406 template<typename OBJ, typename ...ARGS>
407 static l4_msgtag_t
408 call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a)
409 {
410 OPCODE_TYPE op;
411 unsigned limit = tag.words() * Word_bytes;
412 using S = Svr_xmit<OPCODE_TYPE>;
413 l4_ret_t err = S::to_svr(reinterpret_cast<char *>(l4_utcb_mr_u(utcb)->mr), 0,
414 limit, op, Dir_in(), Cls_data());
415 if (L4_UNLIKELY(err < 0))
416 return l4_msgtag(-L4_EMSGTOOSHORT, 0, 0, 0);
417
418 return _call<OBJ, ARGS...>(o, utcb, tag, rights, op, a...);
419 }
420};
421
422template<>
423struct Dispatch_call<Typeid::Detail::Rpcs_end, void>
424{
425 template<typename OBJ, typename ...ARGS>
426 static l4_msgtag_t
427 _call(OBJ *, l4_utcb_t *, l4_msgtag_t, unsigned, int, ARGS ...)
428 { return l4_msgtag(-L4_ENOSYS, 0, 0, 0); }
429
430 template<typename OBJ, typename ...ARGS>
431 static l4_msgtag_t
432 call(OBJ *, l4_utcb_t *, l4_msgtag_t, unsigned, ARGS ...)
433 { return l4_msgtag(-L4_ENOSYS, 0, 0, 0); }
434};
435
436template<typename OPCODE_TYPE>
437struct Dispatch_call<Typeid::Detail::Rpcs_end, OPCODE_TYPE> :
438 Dispatch_call<Typeid::Detail::Rpcs_end, void> {};
439
440template<typename RPCS, typename OBJ, typename ...ARGS>
441static l4_msgtag_t
442dispatch_call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a)
443{
444 return Dispatch_call<typename RPCS::type, typename RPCS::opcode_type>::template
445 call<OBJ, ARGS...>(o, utcb, tag, rights, a...);
446}
447
448} // namespace Msg
449} // namespace Ipc
450} // namespace L4
Type information handling.
l4_ret_t reply(l4_msgtag_t tag, l4_utcb_t *utcb=l4_utcb()) noexcept
Reply with this reply capability.
Definition capability.h:589
Error codes.
@ L4_ENOSYS
No sys.
Definition err.h:51
@ L4_ENOREPLY
No reply.
Definition err.h:56
@ L4_EMSGTOOLONG
Message too long.
Definition err.h:58
@ L4_EMSGERRRANGE
Error code range error.
Definition err.h:60
@ L4_EPERM
No permission.
Definition err.h:34
@ L4_EMSGTOOSHORT
Message too short.
Definition err.h:57
l4_msgtag_t l4_msgtag(long label, unsigned words, unsigned items, unsigned flags) L4_NOTHROW
Create a message tag from the specified values.
Definition types.h:426
struct l4_utcb_t l4_utcb_t
Opaque type for the UTCB.
Definition utcb.h:56
l4_utcb_t * l4_utcb(void) L4_NOTHROW L4_PURE
Get the UTCB address.
Definition utcb.h:369
union l4_msg_regs_t l4_msg_regs_t
Encapsulation of the message-register block in the UTCB.
#define L4_UNLIKELY(x)
Expression is unlikely to execute.
Definition compiler.h:295
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:294
Interface Definition Language.
l4_int16_t l4_ret_t
Return value of an IPC call as well as an RPC call.
Definition types.h:28
IPC Message related functionality.
Definition ipc_array:154
@ Item_words
number of message words for one message item
Definition ipc_basics:90
@ Mr_bytes
number of bytes available in the UTCB message registers
Definition ipc_basics:96
@ Item_bytes
number of bytes for one message item
Definition ipc_basics:92
@ Word_bytes
number of bytes for one message word
Definition ipc_basics:88
@ Mr_words
number of message words available in the UTCB
Definition ipc_basics:94
IPC related functionality.
Definition ipc_array:13
L4 low-level kernel interface.
Server-side RPC arguments data structure used to provide arguments to the server-side implementation ...
Definition ipc_server:189
Message tag data structure.
Definition types.h:266
unsigned words() const L4_NOTHROW
Get the number of untyped words.
Definition types.h:274
unsigned items() const L4_NOTHROW
Get the number of typed items.
Definition types.h:276
Low-level assert implementation.
#define l4_assert(expr)
Low-level assert.
Definition assert.h:32
Encapsulation of the message-register block in the UTCB.
Definition utcb.h:133
l4_umword_t mr[L4_UTCB_GENERIC_DATA_SIZE]
Message registers.
Definition utcb.h:134