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<Phase PHASE>
32 unsigned get(char *, unsigned offset, unsigned)
33 { return offset; }
34
35 unsigned reserve_data(char *, unsigned offset, unsigned)
36 { return offset; }
37
38 template<Phase PHASE>
39 unsigned set(char *, unsigned offset, unsigned)
40 { return offset; }
41
42 template<typename F, typename ...ARGS>
43 decltype(auto) call(F f, ARGS ...args)
44 { return f(args...); }
45
46 template<typename ...>
47 void reply()
48 {}
49
50 template<typename O, typename FUNC, typename ...ARGS>
51 auto obj_call(O *o, ARGS ...args)
52 -> decltype(typename FUNC::template fwd<O>(o).template call<ARGS...>(args...))
53 {
54 using Fwd = typename FUNC::template fwd<O>;
55 using Idl_ret = typename FUNC::result_type;
56 using Svr_ret = decltype(Fwd(o).template call<ARGS...>(args...));
57 auto ret = Fwd(o).template call<ARGS...>(args...);
58
59 using L4::Types::numeric_limits;
60 // Truncating a negative return value which is too small could result in a
61 // positive value pretending "success" to the caller.
62 if constexpr (numeric_limits<Svr_ret>::min() < numeric_limits<Idl_ret>::min())
63 {
64 if (L4_UNLIKELY(ret < numeric_limits<Idl_ret>::min()))
65 return -L4_EMSGERRRANGE;
66 }
67 // Truncating a positive return value which is too large results either in a
68 // positive value different from the original return value (but interpreted
69 // as "success" by the caller) or in a negative value. Trigger an assertion
70 // in these cases but also return an error in case of NDEBUG=y.
71 if constexpr (numeric_limits<Svr_ret>::max() > numeric_limits<Idl_ret>::max())
72 {
73 l4_assert(ret <= numeric_limits<Idl_ret>::max());
74 if (L4_UNLIKELY(ret > numeric_limits<Idl_ret>::max()))
75 return -L4_EMSGERRRANGE;
76 }
77 // The following two checks are required in case the Idl_ret limits exceed
78 // the limits of the 'label' part of l4_msgtag_t.
79 if constexpr (numeric_limits<Svr_ret>::min() < numeric_limits<l4_msgtag_t>::min())
80 {
81 if (L4_UNLIKELY(ret < numeric_limits<l4_msgtag_t>::min()))
82 return -L4_EMSGERRRANGE;
83 }
84 if constexpr (numeric_limits<Svr_ret>::max() > numeric_limits<l4_msgtag_t>::max())
85 {
86 l4_assert(ret <= numeric_limits<l4_msgtag_t>::max());
87 if (L4_UNLIKELY(ret > numeric_limits<l4_msgtag_t>::max()))
88 return -L4_EMSGERRRANGE;
89 }
90
91 return ret;
92 }
93};
94
98template<typename T, typename SVR_TYPE, typename ...M>
99struct Svr_arg : Svr_xmit<T>, Arg_pack<M...>
100{
101 using Base = Arg_pack<M...>;
102
103 using svr_type = SVR_TYPE;
104 using svr_arg_type = typename _Elem<T>::svr_arg_type;
105 using xmit = Svr_xmit<T>;
106 static constexpr bool Is_input = _Elem<T>::Is_input;
107 static constexpr bool Is_output = _Elem<T>::Is_output;
108
109 svr_type v;
110
111 template<Phase PHASE>
112 int get(char *msg, unsigned offset, unsigned limit)
113 {
114 int r = offset;
115 if constexpr(Is_input)
116 {
117 static_assert(PHASE == Phase::Data || PHASE == Phase::Items);
118 if constexpr (PHASE == Phase::Data)
119 r = xmit::to_svr_data(msg, offset, limit, this->v);
120 else // PHASE == Phase::Items
121 r = xmit::to_svr_items(msg, offset, limit, this->v);
122 }
123
124 if (L4_LIKELY(r >= 0))
125 return Base::template get<PHASE>(msg, r, limit);
126
128 {
129 v = svr_type();
130 return Base::template get<PHASE>(msg, offset, limit);
131 }
132
133 return r;
134 }
135
136 int reserve_data(char *msg, unsigned offset, unsigned limit)
137 {
138 int r = offset;
139 if constexpr (Is_output)
140 r = xmit::reserve_data(msg, offset, limit, this->v);
141
142 if (L4_LIKELY(r >= 0))
143 return Base::reserve_data(msg, r, limit);
144
145 return r;
146 }
147
148 template<Phase PHASE>
149 int set(char *msg, unsigned offset, unsigned limit)
150 {
151 int r = offset;
152 if constexpr (Is_output)
153 {
154 static_assert(PHASE == Phase::Data || PHASE == Phase::Items);
155 if constexpr (PHASE == Phase::Data)
156 r = xmit::from_svr_data(msg, offset, limit, this->v);
157 else // PHASE == Phase::Items
158 r = xmit::from_svr_items(msg, offset, limit, this->v);
159 }
160
161 if (L4_UNLIKELY(r < 0))
162 return r;
163
164 return Base::template set<PHASE>(msg, r, limit);
165 }
166
167 template<typename F, typename ...ARGS>
168 decltype(auto) call(F f, ARGS ...args)
169 {
170 //As_arg<value_type> check;
171 return Base::template
172 call<F, ARGS..., svr_arg_type>(f, args..., this->v);
173 }
174
175 template<typename ...ARGS>
176 void reply(ARGS &&...args)
177 {
178 // Only consume out parameters or parameters of type In_out.
179 if constexpr (Is_output)
180 reply_consume_arg<ARGS...>(L4::Types::forward<ARGS>(args)...);
181 else
182 Base::template reply<ARGS...>(L4::Types::forward<ARGS>(args)...);
183 }
184
185 template<typename O, typename FUNC, typename ...ARGS>
186 decltype(auto) obj_call(O *o, ARGS ...args)
187 {
188 //As_arg<value_type> check;
189 return Base::template
190 obj_call<O, FUNC, ARGS..., svr_arg_type>(o, args..., this->v);
191 }
192
193private:
194 template<typename A, typename ...ARGS>
195 void reply_consume_arg(A &&a, ARGS &&...args)
196 {
197 this->v = L4::Types::move(a);
198 Base::template reply<ARGS...>(L4::Types::forward<ARGS>(args)...);
199 }
200};
201
207template<typename T, typename ...M>
208struct Svr_arg<T, void, M...> : Arg_pack<M...>
209{};
210
211template<typename A, typename ...M>
212struct Arg_pack<A, M...> : Svr_arg<A, typename _Elem<A>::svr_type, M...>
213{};
214
215} // namespace Detail
216
217//---------------------------------------------------------------------
222template<typename IPC_TYPE> struct Svr_arg_pack;
223
224template<typename R, typename ...ARGS>
225struct Svr_arg_pack<R (ARGS...)> : Detail::Arg_pack<ARGS...>
226{
227 using Base = Detail::Arg_pack<ARGS...>;
228
229 template<Phase PHASE>
230 int get(void *msg, unsigned offset, unsigned limit)
231 {
232 char *buf = static_cast<char *>(msg);
233 return Base::template get<PHASE>(buf, offset, limit);
234 }
235
236 int reserve_data(void *msg, unsigned offset, unsigned limit)
237 {
238 char *buf = static_cast<char *>(msg);
239 return Base::reserve_data(buf, offset, limit);
240 }
241
242 template<Phase PHASE>
243 int set(void *msg, unsigned offset, unsigned limit)
244 {
245 char *buf = static_cast<char *>(msg);
246 return Base::template set<PHASE>(buf, offset, limit);
247 }
248};
249
253template<typename IPC_TYPE, typename O, typename ...ARGS>
254static l4_msgtag_t
255handle_svr_obj_call(O *o, l4_utcb_t *utcb, l4_msgtag_t tag, ARGS ...args)
256{
257 using Pack = Svr_arg_pack<typename IPC_TYPE::rpc::ipc_type>;
258 enum
259 {
260 Do_reply = IPC_TYPE::rpc::flags_type::Is_call,
261 Short_err = Do_reply ? -L4_EMSGTOOSHORT : -L4_ENOREPLY,
262 };
263
264 // XXX: send a reply or just do not reply in case of a cheating client
265 if (L4_UNLIKELY(tag.words() + tag.items() * Item_words > Mr_words))
266 return l4_msgtag(Short_err, 0, 0, 0);
267
268 // our whole arguments data structure
269 Pack pack;
270 l4_msg_regs_t *mrs = l4_utcb_mr_u(utcb);
271
272 int in_pos = Detail::Sizeof<typename IPC_TYPE::opcode_type>::size;
273
274 unsigned const in_bytes = tag.words() * Word_bytes;
275
276 in_pos = pack.template get<Phase::Data>(&mrs->mr[0], in_pos, in_bytes);
277
278 if (L4_UNLIKELY(in_pos < 0))
279 return l4_msgtag(Short_err, 0, 0, 0);
280
281 if (L4_UNLIKELY(pack.reserve_data(mrs->mr, 0, Mr_bytes) < 0))
282 return l4_msgtag(Short_err, 0, 0, 0);
283
284
285 in_pos = pack.template get<Phase::Items>(&mrs->mr[tag.words()], 0,
286 tag.items() * Item_bytes);
287
288 if (L4_UNLIKELY(in_pos < 0))
289 return l4_msgtag(Short_err, 0, 0, 0);
290
291 asm volatile ("" : "=m" (mrs->mr));
292
293 // call the server function
294 auto ret = pack.template obj_call<O, typename IPC_TYPE::rpc, ARGS...>(o, args...);
295
296 if (!Do_reply)
297 return l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
298
299 // our convention says that negative return value means no
300 // reply data
301 if (L4_UNLIKELY(ret < 0))
302 return l4_msgtag(ret, 0, 0, 0);
303
304 // reply with the reply data from the server function
305 int bytes = pack.template set<Phase::Data>(mrs->mr, 0, Mr_bytes);
306 if (L4_UNLIKELY(bytes < 0))
307 return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
308
309 unsigned words = (bytes + Word_bytes - 1) / Word_bytes;
310 bytes = pack.template set<Phase::Items>(&mrs->mr[words], 0,
311 Mr_bytes - words * Word_bytes);
312 if (L4_UNLIKELY(bytes < 0))
313 return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
314
315 unsigned const items = bytes / Item_bytes;
316 return l4_msgtag(ret, words, items, 0);
317}
318
319
320template<typename RPC, typename ...OUT_ARGS>
321static l4_msgtag_t
322pack_svr_reply(l4_utcb_t *utcb, l4_ret_t ret, OUT_ARGS && ...args)
323{
324 static_assert(RPC::flags_type::Is_call, "RPC to reply to must be a call.");
325
326 // our convention says that negative return value means no
327 // reply data
328 if (L4_UNLIKELY(ret < 0))
329 return l4_msgtag(ret, 0, 0, 0);
330
331 // TODO: Could try to filter out Dir_in parameters already in the arg pack.
333
334 // our whole arguments data structure
335 Pack pack;
336 l4_msg_regs_t *mrs = l4_utcb_mr_u(utcb);
337
338 // Reserves spaces for all data output arguments.
339 if (L4_UNLIKELY(pack.reserve_data(mrs->mr, 0, Mr_bytes) < 0))
340 return l4_msgtag(-L4_EMSGTOOSHORT, 0, 0, 0);
341
342 // call the server reply function
343 pack.template reply<OUT_ARGS...>(L4::Types::forward<OUT_ARGS>(args)...);
344
345 // reply with the reply data from the server function
346 int bytes = pack.template set<Phase::Data>(mrs->mr, 0, Mr_bytes);
347 if (L4_UNLIKELY(bytes < 0))
348 return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
349
350 unsigned words = (bytes + Word_bytes - 1) / Word_bytes;
351 bytes = pack.template set<Phase::Items>(&mrs->mr[words], 0,
352 Mr_bytes - words * Word_bytes);
353 if (L4_UNLIKELY(bytes < 0))
354 return l4_msgtag(-L4_EMSGTOOLONG, 0, 0, 0);
355
356 unsigned const items = bytes / Item_bytes;
357 return l4_msgtag(ret, words, items, 0);
358}
359
373template<typename RPC, typename ...OUT_ARGS>
374static l4_ret_t
375reply(L4::Reply_cap reply_cap, l4_ret_t res, OUT_ARGS && ...args)
376{
377 l4_utcb_t *u = l4_utcb();
378 return reply_cap.reply(
379 L4::Ipc::Msg::pack_svr_reply<RPC, OUT_ARGS...>(
380 u, res, L4::Types::forward<OUT_ARGS>(args)...),
381 u);
382}
383
384//-------------------------------------------------------------------------
385
386template<typename RPCS, typename OPCODE_TYPE>
387struct Dispatch_call;
388
389template<typename CLASS>
390struct Dispatch_call<L4::Typeid::Raw_ipc<CLASS>, void>
391{
392 template<typename OBJ, typename ...ARGS>
393 static l4_msgtag_t
394 call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, ARGS ...a)
395 {
396 return o->op_dispatch(utcb, tag, a...);
397 }
398};
399
400template<typename RPCS>
401struct Dispatch_call<RPCS, void>
402{
403 constexpr static unsigned rmask()
404 { return RPCS::rpc::flags_type::Rights & 3UL; }
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 if ((rights & rmask()) != rmask())
411 return l4_msgtag(-L4_EPERM, 0, 0, 0);
412
413 using Rights = L4::Typeid::Rights<typename RPCS::rpc::class_type>;
414 return handle_svr_obj_call<RPCS>(o, utcb, tag,
415 Rights(rights), a...);
416
417 }
418};
419
420template<typename RPCS, typename OPCODE_TYPE>
421struct Dispatch_call
422{
423 constexpr static unsigned rmask()
424 { return RPCS::rpc::flags_type::Rights & 3UL; }
425
426 template<typename OBJ, typename ...ARGS>
427 static l4_msgtag_t
428 _call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, OPCODE_TYPE op, ARGS ...a)
429 {
430 if (L4::Types::Same_v<typename RPCS::opcode_type, void>
431 || RPCS::Opcode == op)
432 {
433 if ((rights & rmask()) != rmask())
434 return l4_msgtag(-L4_EPERM, 0, 0, 0);
435
436 using Rights = L4::Typeid::Rights<typename RPCS::rpc::class_type>;
437 return handle_svr_obj_call<RPCS>(o, utcb, tag,
438 Rights(rights), a...);
439 }
440 return Dispatch_call<typename RPCS::next, OPCODE_TYPE>::template
441 _call<OBJ, ARGS...>(o, utcb, tag, rights, op, a...);
442 }
443
444 template<typename OBJ, typename ...ARGS>
445 static l4_msgtag_t
446 call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a)
447 {
448 OPCODE_TYPE op;
449 unsigned limit = tag.words() * Word_bytes;
450 using xmit = Svr_xmit<OPCODE_TYPE>;
451 auto *mrs = reinterpret_cast<char *>(l4_utcb_mr_u(utcb)->mr);
452 l4_ret_t err = xmit::to_svr_data(mrs, 0, limit, op);
453 if (L4_UNLIKELY(err < 0))
454 return l4_msgtag(-L4_EMSGTOOSHORT, 0, 0, 0);
455
456 return _call<OBJ, ARGS...>(o, utcb, tag, rights, op, a...);
457 }
458};
459
460template<>
461struct Dispatch_call<Typeid::Detail::Rpcs_end, void>
462{
463 template<typename OBJ, typename ...ARGS>
464 static l4_msgtag_t
465 _call(OBJ *, l4_utcb_t *, l4_msgtag_t, unsigned, int, ARGS ...)
466 { return l4_msgtag(-L4_ENOSYS, 0, 0, 0); }
467
468 template<typename OBJ, typename ...ARGS>
469 static l4_msgtag_t
470 call(OBJ *, l4_utcb_t *, l4_msgtag_t, unsigned, ARGS ...)
471 { return l4_msgtag(-L4_ENOSYS, 0, 0, 0); }
472};
473
474template<typename OPCODE_TYPE>
475struct Dispatch_call<Typeid::Detail::Rpcs_end, OPCODE_TYPE> :
476 Dispatch_call<Typeid::Detail::Rpcs_end, void> {};
477
478template<typename RPCS, typename OBJ, typename ...ARGS>
479static l4_msgtag_t
480dispatch_call(OBJ *o, l4_utcb_t *utcb, l4_msgtag_t tag, unsigned rights, ARGS ...a)
481{
482 return Dispatch_call<typename RPCS::type, typename RPCS::opcode_type>::template
483 call<OBJ, ARGS...>(o, utcb, tag, rights, a...);
484}
485
486} // namespace Msg
487} // namespace Ipc
488} // 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:607
Error codes.
@ L4_ENOSYS
No sys.
Definition err.h:52
@ L4_ENOREPLY
No reply.
Definition err.h:57
@ L4_EMSGTOOLONG
Message too long.
Definition err.h:59
@ L4_EMSGERRRANGE
Error code range error.
Definition err.h:61
@ L4_EPERM
No permission.
Definition err.h:34
@ L4_EMSGTOOSHORT
Message too short.
Definition err.h:58
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:441
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:29
IPC Message related functionality.
Definition ipc_array:154
@ Items
First phase: copy bytes to/from MRs.
Definition ipc_basics:145
@ Item_words
number of message words for one message item
Definition ipc_basics:87
@ Mr_bytes
number of bytes available in the UTCB message registers
Definition ipc_basics:93
@ Item_bytes
number of bytes for one message item
Definition ipc_basics:89
@ Word_bytes
number of bytes for one message word
Definition ipc_basics:85
@ Mr_words
number of message words available in the UTCB
Definition ipc_basics:91
IPC related functionality.
Definition ipc_array:13
L4 low-level kernel interface.
static constexpr bool Is_output
Is the argument transferred from server to client?
Definition ipc_basics:484
static constexpr bool Is_optional
Is it an optional argument?
Definition ipc_basics:480
static constexpr bool Is_input
Is the argument transferred from client to server?
Definition ipc_basics:482
T svr_arg_type
The argument type for the server-side function.
Definition ipc_basics:478
Server-side RPC arguments data structure used to provide arguments to the server-side implementation ...
Definition ipc_server:222
static constexpr int to_svr_items(char *msg, unsigned offset, unsigned limit, MTYPE &arg) noexcept
Definition ipc_basics:386
static constexpr int from_svr_items(char *msg, unsigned offset, unsigned limit, MTYPE const &arg) noexcept
Definition ipc_basics:328
static int to_svr_data(char *msg, unsigned offset, unsigned limit, MTYPE &arg) noexcept
Copy data from the message to a T server reference (arg must be MTYPE &).
Definition ipc_basics:444
static int from_svr_data(char *msg, unsigned offset, unsigned limit, MTYPE const &arg) noexcept
Copy a T into the message (arg must be either MTYPE or MTYPE const &).
Definition ipc_basics:459
static int reserve_data(char *, unsigned offs, unsigned limit, MTYPE &) noexcept
Prepare a T output operand (arg must be MTYPE &).
Definition ipc_basics:449
Message tag data structure.
Definition types.h:269
unsigned words() const L4_NOTHROW
Get the number of untyped words.
Definition types.h:277
unsigned items() const L4_NOTHROW
Get the number of typed items.
Definition types.h:279
Low-level assert implementation.
#define l4_assert(expr)
Low-level assert.
Definition assert.h:32
l4_umword_t mr[L4_UTCB_GENERIC_DATA_SIZE]
Message registers.
Definition utcb.h:134