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
19#pragma once
20
21#include <l4/re/util/cap_alloc>
22#include <l4/sys/cxx/ipc_server_loop>
23#include <l4/cxx/ipc_timeout_queue>
24#include <l4/sys/assert.h>
25
26namespace L4Re { namespace Util {
27
28/**
29 * \brief Buffer-register (BR) manager for L4::Server.
30 * \ingroup api_l4re_util
31 *
32 * Implementation of the L4::Ipc_svr::Server_iface API for managing the
33 * server-side receive buffers needed for a set of server objects running
34 * within a server.
35 */
36class Br_manager : public L4::Ipc_svr::Server_iface
37{
38private:
39  enum { _mem = 0, _ports = 0 };
40
41public:
42  /// Make a buffer-register (BR) manager
43  Br_manager() : _caps(0), _cap_flags(L4_RCV_ITEM_LOCAL_ID) {}
44
45  Br_manager(Br_manager const &) = delete;
46  Br_manager &operator = (Br_manager const &) = delete;
47
48  Br_manager(Br_manager &&) = default;
49  Br_manager &operator = (Br_manager &&) = default;
50
51  ~Br_manager()
52  {
53    // Slots for received capabilities are placed at the beginning of the
54    // (shadowed) buffer registers. Free those.
55    for (unsigned i = 0; i < _caps; ++i)
56      cap_alloc.free(L4::Cap<void>(_brs[i] & L4_CAP_MASK));
57  }
58
59  /*
60   * This implementation dynamically manages assignment of buffer registers for
61   * the necessary amount of receive buffers allocated by all calls to this
62   * function.
63   */
64  int alloc_buffer_demand(Demand const &d)
65  {
66    using L4::Ipc::Small_buf;
67
68    // memory and IO port receive windows currently not supported
69    if (d.mem || d.ports)
70      return -L4_EINVAL;
71
72    // take two extra buffers for a possible timeout and a zero terminator
73    if (d.caps + d.mem * 2 + d.ports * 2 + 3 >= L4_UTCB_GENERIC_BUFFERS_SIZE)
74      return -L4_ERANGE;
75
76    if (d.caps > _caps)
77      {
78        while (_caps < d.caps)
79          {
80            L4::Cap<void> cap = cap_alloc.alloc();
81            if (!cap)
82              return -L4_ENOMEM;
83
84            reinterpret_cast<Small_buf&>(_brs[_caps])
85              = Small_buf(cap.cap(), _cap_flags);
86            ++_caps;
87          }
88        _brs[_caps] = 0;
89      }
90
91    return L4_EOK;
92  }
93
94
95  L4::Cap<void> get_rcv_cap(int i) const
96  {
97    if (i < 0 || i >= _caps)
98      return L4::Cap<void>::Invalid;
99
100    return L4::Cap<void>(_brs[i] & L4_CAP_MASK);
101  }
102
103  int realloc_rcv_cap(int i)
104  {
105    using L4::Ipc::Small_buf;
106
107    if (i < 0 || i >= _caps)
108      return -L4_EINVAL;
109
110    L4::Cap<void> cap = cap_alloc.alloc();
111    if (!cap)
112      return -L4_ENOMEM;
113
114    reinterpret_cast<Small_buf&>(_brs[i])
115      = Small_buf(cap.cap(), _cap_flags);
116
117    return L4_EOK;
118  }
119
120  /**
121   * Set the receive flags for the buffers.
122   *
123   * \pre Must be called before any handlers are registered.
124   *
125   * \param flags New receive capability flags, see #l4_msg_item_consts_t.
126   */
127  void set_rcv_cap_flags(unsigned long flags)
128  {
129    l4_assert(_caps == 0);
130
131    _cap_flags = flags;
132  }
133
134  /// No timeouts handled by us.
135  int add_timeout(L4::Ipc_svr::Timeout *, l4_kernel_clock_t)
136  { return -L4_ENOSYS; }
137
138  /// No timeouts handled by us.
139  int remove_timeout(L4::Ipc_svr::Timeout *)
140  { return -L4_ENOSYS; }
141
142  /// setup_wait() used the server loop (L4::Server)
143  void setup_wait(l4_utcb_t *utcb, L4::Ipc_svr::Reply_mode)
144  {
145    l4_buf_regs_t *br = l4_utcb_br_u(utcb);
146    br->bdr = 0;
147    for (unsigned i = 0; i <= _caps; ++i)
148      br->br[i] = _brs[i];
149  }
150
151protected:
152  /// Used for assigning BRs for a timeout
153  unsigned first_free_br() const
154  {
155    // we take the last BR here (this is c constant),
156    return L4_UTCB_GENERIC_BUFFERS_SIZE - 1;
157    // could also take _caps + _mem +_ports + 1 (would be dynamic)
158    // return _caps + _mem + _ports + 1;
159  }
160
161private:
162  unsigned short _caps;
163  unsigned long _cap_flags;
164
165  l4_umword_t _brs[L4_UTCB_GENERIC_BUFFERS_SIZE];
166};
167
168/**
169 * Predefined server-loop hooks for a server loop using the Br_manager.
170 *
171 * This class can be used whenever a server loop including full management of
172 * receive  buffer resources is needed.
173 */
174struct Br_manager_hooks
175: L4::Ipc_svr::Ignore_errors,
176  L4::Ipc_svr::Default_timeout,
177  L4::Ipc_svr::Compound_reply,
178  Br_manager
179{};
180
181/**
182 * Predefined server-loop hooks for a server with using the Br_manager and
183 * a timeout queue.
184 *
185 * This class can be used for server loops that need the full package of
186 * buffer-register management and a timeout queue.
187 */
188struct Br_manager_timeout_hooks :
189  public L4::Ipc_svr::Timeout_queue_hooks<Br_manager_timeout_hooks, Br_manager>,
190  public L4::Ipc_svr::Ignore_errors
191{
192public:
193  static l4_kernel_clock_t now()
194  { return l4_kip_clock(l4re_kip()); }
195};
196
197}}
198
199