1// vi:set ft=cpp: -*- Mode: C++ -*-
2/*
3 * Copyright (C) 2015 Kernkonzept GmbH.
4 * Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
5 *
6 * This file is distributed under the terms of the GNU General Public
7 * License, version 2.  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 "ipc_epiface"
21
22namespace L4 {
23
24/**
25 * \ingroup client_server_ipc
26 *
27 * Server-Side framework for implementing object-oriented servers.´
28 * \defgroup cxx_ipc_server Server-Side IPC framework
29 */
30/**
31 * \ingroup cxx_ipc_server
32 *
33 * Helper classes for L4::Server instantiation.
34 */
35namespace Ipc_svr {
36
37/**
38 * \ingroup cxx_ipc_server
39 *
40 * Reply mode for server loop.
41 *
42 * The reply mode specifies if the server loop shall do a compound reply
43 * and wait operation (#Reply_compound), which is the most performant
44 * method.  Note, setup_wait() is called before the reply.  The other
45 * way is to call reply and wait separately and call setup_wait in between.
46 *
47 * The actual mode is determined by the return value of the before_reply()
48 * hook in the LOOP_HOOKS of L4::Server.
49 */
50enum Reply_mode
51{
52  Reply_compound, ///< Server shall use a compound reply and wait (fast).
53  Reply_separate  ///< Server shall call reply and wait separately.
54};
55
56/**
57 * \ingroup cxx_ipc_server
58 *
59 * Mix in for LOOP_HOOKS to ignore IPC errors.
60 */
61struct Ignore_errors
62{ static void error(l4_msgtag_t, l4_utcb_t *) {} };
63
64/**
65 * \ingroup cxx_ipc_server
66 *
67 * Mix in for LOOP_HOOKS to use a 0 send and a infinite receive timeout.
68 */
69struct Default_timeout
70{ static l4_timeout_t timeout() { return L4_IPC_SEND_TIMEOUT_0; } };
71
72/**
73 * \ingroup cxx_ipc_server
74 *
75 * Mix in for LOOP_HOOKS to always use compound reply and wait.
76 */
77struct Compound_reply
78{
79  static Reply_mode before_reply(l4_msgtag_t, l4_utcb_t *)
80  { return Reply_compound; }
81};
82
83/**
84 * \ingroup cxx_ipc_server
85 *
86 * Mix in for LOOP_HOOKS for setup_wait no op.
87 */
88struct Default_setup_wait
89{ static void setup_wait(l4_utcb_t *, Reply_mode) {} };
90
91/**
92 * DEPRECATED
93 * \ingroup cxx_ipc_server
94 * \deprecated Use L4::Ipc_svr::Timeout_queue_hooks
95 */
96template< typename HOOKS >
97class Timed_work : public HOOKS
98{
99protected:
100  l4_cpu_time_t _timeout;
101
102public:
103  Timed_work()
104  : _timeout(HOOKS::next_timeout(HOOKS::current_time())) {}
105
106  l4_timeout_t timeout()
107  {
108    return l4_timeout(L4_IPC_TIMEOUT_0,
109                      l4_timeout_abs(_timeout, this->timeout_br()));
110  }
111
112  void setup_wait(l4_utcb_t *utcb, Reply_mode mode)
113  {
114    if (_timeout <= this->current_time()
115        && mode == Reply_separate)
116      {
117	this->work();
118        _timeout = this->next_timeout(_timeout);
119      }
120    HOOKS::setup_wait(utcb, mode);
121  }
122
123  Reply_mode before_reply(l4_msgtag_t, l4_utcb_t *)
124  {
125    if (_timeout <= this->current_time())
126      return Reply_separate;
127    return Reply_compound;
128  }
129};
130
131/**
132 * Direct dispatch helper, for forwarding dispatch calls to
133 * a registry \a R.
134 * \tparam R  Data type of the registry that is used for dispatching to
135 *            different server objects, usually based on the protected IPC
136 *            label.
137 */
138template< typename R >
139struct Direct_dispatch
140{
141  /// stores a reference to the registry object
142  R &r;
143
144  /// Make a direct dispatcher
145  Direct_dispatch(R &r) : r(r) {}
146
147  /// call operator forwarding to r.dispatch()
148  l4_msgtag_t operator () (l4_msgtag_t tag, l4_umword_t obj, l4_utcb_t *utcb)
149  { return r.dispatch(tag, obj, utcb); }
150};
151
152/**
153 * Direct dispatch helper, for forwarding dispatch calls via a pointer to
154 * a registry \a R.
155 * \tparam R  Data type of the registry that is used for dispatching to
156 *            different server objects, usually based on the protected IPC
157 *            label.
158 */
159template< typename R >
160struct Direct_dispatch<R*>
161{
162  /// stores a pointer to the registry object
163  R *r;
164
165  /// Make a direct dispatcher
166  Direct_dispatch(R *r) : r(r) {}
167
168  /// call operator forwarding to r->dispatch()
169  l4_msgtag_t operator () (l4_msgtag_t tag, l4_umword_t obj, l4_utcb_t *utcb)
170  { return r->dispatch(tag, obj, utcb); }
171};
172
173#ifdef __EXCEPTIONS
174/**
175 * Dispatch helper wrapping try {} catch {} around the dispatch call.
176 * \tparam R    Data type of the registry used for dispatching to objects.
177 * \tparam Exc  Data type of the exceptions that shall be catched.
178 *              This data type must provide a member err_no() that returns
179 *              the negative integer (int) error code for the exception.
180 *
181 * This dispatcher wraps Direct_dispatch<R> with a try-catch (Exc).
182 */
183template< typename R, typename Exc> // = L4::Runtime_error>
184struct Exc_dispatch : private Direct_dispatch<R>
185{
186  /// Make an exception handling dispatcher
187  Exc_dispatch(R r) : Direct_dispatch<R>(r) {}
188
189  /**
190   * Dispatch the call via Direct_dispatch<R>() and handle exceptions.
191   */
192  l4_msgtag_t operator () (l4_msgtag_t tag, l4_umword_t obj, l4_utcb_t *utcb)
193  {
194    try
195      {
196        return Direct_dispatch<R>::operator () (tag, obj, utcb);
197      }
198    catch (Exc &e)
199      {
200        return l4_msgtag(e.err_no(), 0, 0, 0);
201      }
202    catch (int err)
203      {
204        return l4_msgtag(err, 0, 0, 0);
205      }
206    catch (long err)
207      {
208        return l4_msgtag(err, 0, 0, 0);
209      }
210  }
211};
212#endif
213
214/**
215 * \ingroup cxx_ipc_server
216 *
217 * Empty implementation of Server_iface.
218 *
219 * This implementation of Server_iface provides no buffer or timeout management
220 * at all it just returns errors for all calls that express other than empty
221 * demands.  However, this may be useful for very simple servers that serve
222 * simple server objects only.
223 */
224class Br_manager_no_buffers : public Server_iface
225{
226public:
227  /**
228   * \copydoc  Server_iface::alloc_buffer_demand()
229   * \return success (0) if demand is empty, -L4_ENOMEM else.
230   */
231  int alloc_buffer_demand(Demand const &demand)
232  {
233    if (!demand.no_demand())
234      return -L4_ENOMEM;
235    return L4_EOK;
236  }
237
238  /// Returns L4::Cap<void>::Invalid, we have no buffer management
239  L4::Cap<void> get_rcv_cap(int) const
240  { return L4::Cap<void>::Invalid; }
241
242  /// Returns -L4_ENOMEM, we have no buffer management
243  int realloc_rcv_cap(int)
244  { return -L4_ENOMEM; }
245
246  /// Returns -L4_ENOSYS, we have no timeout queue
247  int add_timeout(Timeout *, l4_kernel_clock_t)
248  { return -L4_ENOSYS; }
249
250  /// Returns -L4_ENOSYS, we have no timeout queue
251  int remove_timeout(Timeout *)
252  { return -L4_ENOSYS; }
253
254protected:
255  /// Returns 1 as first free buffer
256  unsigned first_free_br() const
257  { return 1; }
258
259  /// Setup wait function for the server loop (Server<>).
260  void setup_wait(l4_utcb_t *utcb, L4::Ipc_svr::Reply_mode)
261  {
262    l4_buf_regs_t *br = l4_utcb_br_u(utcb);
263    br->bdr = 0;
264    br->br[0] = 0;
265  }
266};
267
268/**
269 * \ingroup cxx_ipc_server
270 *
271 * Default LOOP_HOOKS.
272 *
273 * Combination of Ignore_errors, Default_timeout, Compound_reply,
274 * and Br_manager_no_buffers.
275 */
276struct Default_loop_hooks :
277  public Ignore_errors, public Default_timeout, public Compound_reply,
278  Br_manager_no_buffers
279{};
280
281}
282
283/**
284 * \ingroup cxx_ipc_server
285 *
286 * Basic server loop for handling client requests.
287 * \param LOOP_HOOKS the server inherits from LOOP_HOOKS and calls the
288 *        hooks defined in LOOP_HOOKS in the server loop.
289 *        See Ipc_svr::Default_loop_hooks, Ipc_svr::Ignore_errors,
290 *        Ipc_svr::Default_timeout, Ipc_svr::Compound_reply, and
291 *        Ipc_svr::Br_manager_no_buffers.
292 *
293 * This is basically a simple server loop that uses a single message buffer
294 * for receiving requests and sending replies. The dispatcher determines
295 * how incoming messages are handled.
296 */
297template< typename LOOP_HOOKS = Ipc_svr::Default_loop_hooks >
298class Server :
299  public LOOP_HOOKS
300{
301public:
302  /**
303   * Initializes the server loop.
304   * \param utcb The UTCB of the thread running the server loop.
305   *
306   * Note that this variant is deprecated. The UTCB can be specified with
307   * the server loop function (l4_utcb() is the default). (2021-10)
308   */
309  /* Internal note: After all users have been converted, remove this
310   * constructor. Also remove the constructor below then. */
311  explicit Server(l4_utcb_t *)
312    L4_DEPRECATED("Do not specify the UTCB with the constructor. "
313	          "Supply it on the loop function if needed.")
314  {}
315
316  /**
317   * Initializes the server loop.
318   */
319  /* Internal note: Remove this constructor when the above deprecated
320   * constructor with the UTCB pointer is also removed. */
321  Server() {}
322
323  /**
324   * The server loop.
325   *
326   * This function usually never returns, it waits for
327   * incoming messages calls the dispatcher, sends a reply and waits again.
328   */
329  template< typename DISPATCH >
330  inline L4_NORETURN void internal_loop(DISPATCH dispatch, l4_utcb_t *);
331
332  /**
333   * Server loop without exception handling.
334   */
335  template< typename R >
336  inline L4_NORETURN void loop_noexc(R r, l4_utcb_t *u = l4_utcb())
337  { internal_loop(Ipc_svr::Direct_dispatch<R>(r), u); }
338
339#ifdef __EXCEPTIONS
340  /**
341   * Server loop with internal exception handling.
342   *
343   * This server loop translates L4::Runtime_error exceptions
344   * into negative error return codes sent to the caller.
345   */
346  template< typename EXC, typename R >
347  inline L4_NORETURN void loop(R r, l4_utcb_t *u = l4_utcb())
348  {
349    internal_loop(Ipc_svr::Exc_dispatch<R, EXC>(r), u);
350    while(1)
351      ;
352  }
353#endif
354protected:
355  /// Internal implementation for reply and wait
356  inline l4_msgtag_t reply_n_wait(l4_msgtag_t reply, l4_umword_t *p, l4_utcb_t *);
357};
358
359template< typename L >
360inline l4_msgtag_t
361Server<L>::reply_n_wait(l4_msgtag_t reply, l4_umword_t *p, l4_utcb_t *utcb)
362{
363  if (reply.label() != -L4_ENOREPLY)
364    {
365      Ipc_svr::Reply_mode m = this->before_reply(reply, utcb);
366      if (m == Ipc_svr::Reply_compound)
367        {
368          this->setup_wait(utcb, m);
369          return l4_ipc_reply_and_wait(utcb, reply, p, this->timeout());
370        }
371      else
372        {
373          l4_msgtag_t res = l4_ipc_send(L4_INVALID_CAP | L4_SYSF_REPLY, utcb, reply, this->timeout());
374          if (res.has_error())
375            return res;
376        }
377    }
378  this->setup_wait(utcb, Ipc_svr::Reply_separate);
379  return l4_ipc_wait(utcb, p, this->timeout());
380}
381
382template< typename L >
383template< typename DISPATCH >
384inline L4_NORETURN void
385Server<L>::internal_loop(DISPATCH dispatch, l4_utcb_t *utcb)
386{
387  l4_msgtag_t res;
388  l4_umword_t p;
389  l4_msgtag_t r = l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
390
391  while (true)
392    {
393      res = reply_n_wait(r, &p, utcb);
394      if (res.has_error())
395        {
396          this->error(res, utcb);
397          r = l4_msgtag(-L4_ENOREPLY, 0, 0, 0);
398          continue;
399        }
400
401      r = dispatch(res, p, utcb);
402    }
403}
404
405} // namespace L4
406