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