summaryrefslogtreecommitdiff
path: root/libviengoos/viengoos/ipc.h
blob: 67c2badc28af4df4f67490b6531505913915acb5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/* ipc.h - Interprocess communication interface.
   Copyright (C) 2008 Free Software Foundation, Inc.
   Written by Neal H. Walfield <neal@gnu.org>.

   This file is part of the GNU Hurd.

   GNU Hurd is free software: you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   GNU Hurd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GNU Hurd.  If not, see
   <http://www.gnu.org/licenses/>.  */

#ifndef _VIENGOOS_IPC_H
#define _VIENGOOS_IPC_H 1

#include <stdint.h>
#include <errno.h>
#include <viengoos/addr.h>
#include <hurd/stddef.h>
#include <viengoos/message.h>
#include <assert.h>
#include <hurd/startup.h>

#ifdef USE_L4
#include <l4.h>
#endif

/* IPC flags.  */
enum
  {
    /* IPC includes a receive phase.  */
    VG_IPC_RECEIVE = 1 << 0,
    /* Don't unblock the receive buffer if there is no message queued
       for delivery.  */
    VG_IPC_RECEIVE_NONBLOCKING = 1 << 1,
    /* Activate the thread on message receipt.  */
    VG_IPC_RECEIVE_ACTIVATE = 1 << 2,
    /* Set the receive messenger's thread to the caller.  */
    VG_IPC_RECEIVE_SET_THREAD_TO_CALLER = 1 << 3,
    /* Set the receive messener's address space root to the
       caller's.  */
    VG_IPC_RECEIVE_SET_ASROOT_TO_CALLERS = 1 << 4,
    /* Whether to receive the message inline.  */
    VG_IPC_RECEIVE_INLINE = 1 << 5,
    /* Whether to receive any capabilities inline when receiving a
       message inline (i.e., when VG_IPC_RECEIVE_INLINE is set).  */
    VG_IPC_RECEIVE_INLINE_CAP1 = 1 << 6,

    /* IPC includes a send phase.  */
    VG_IPC_SEND = 1 << 7,
    /* If the object is blocked, return EWOULDBLOCK.  */
    VG_IPC_SEND_NONBLOCKING = 1 << 8,
    /* Activate the thread on message transfer.  */
    VG_IPC_SEND_ACTIVATE = 1 << 9,
    /* Set the send messenger's thread to the caller.  */
    VG_IPC_SEND_SET_THREAD_TO_CALLER = 1 << 10,
    /* Set the sender messener's address space root to the
       caller's.  */
    VG_IPC_SEND_SET_ASROOT_TO_CALLERS = 1 << 11,
    /* Whether to send the message inline.  */
    VG_IPC_SEND_INLINE = 1 << 12,

    /* Which inline data to transfer when sending a message.  Inline
       data is ignored if the send buffer is not ADDR_VOID.  */
    VG_IPC_SEND_INLINE_WORD1 = 1 << 13,
    VG_IPC_SEND_INLINE_WORD2 = 1 << 14,
    VG_IPC_SEND_INLINE_CAP1 = 1 << 15,


    /* The IPC includes a return phase.  */
    VG_IPC_RETURN = 1 << 16,

  };

#ifndef RM_INTERN
/* An IPC consists of three phases: the receive phase, the send phase
   and the return phase.  All three phases are optional.  Each phase
   is executed after the previous phase has completed.  If a phase
   does not complete successfully, the phase is aborted and the
   remaining phases are not executed.


   RECEIVE PHASE

   If FLAGS contains VG_IPC_RECEIVE, the IPC includes a receive phase.

   If RECV_BUF is not ADDR_VOID, associates RECV_BUF with
   RECV_MESSENGER.

   If FLAGS contains VG_IPC_RECEIVE_NONBLOCKING:

     Unblocks RECV_MESSENGER if RECV_MESSENGER has a messenger waiting
     to deliver a message.  Otherwise, returns EWOUDBLOCK.

   Otherwise:

     Unblocks RECV_MESSENGER.

   Resources are charged to RECV_ACTIVITY.

   If VG_IPC_RECEIVE_ACTIVATE is set, an activation is sent to the
   thread associated with RECV_MESSENGER when RECV_MESSENGER receives
   a message.


   SEND PHASE

   If FLAGS contains VG_IPC_SEND, the IPC includes a send phase.

   If SEND_MESSENGER is ADDR_VOID, an implicit messenger is allocated
   and VG_IPC_SEND_NONBLOCKING is assumed to be on.

   If SEND_BUF is not ADDR_VOID, assocaiates SEND_BUF with
   SEND_MESSENGER.  Otherwise, associates inline data (INLINE_WORD1,
   INLINE_WORD2 and INLINE_CAP) according to the inline flags with
   SEND_MESSENGER.

   If FLAGS contains VG_IPC_SEND_NONBLOCKING:

     If TARGET_MESSENGER is blocked, returns ETIMEDOUT.

   Otherwise:

     Blocks SEND_MESSENGER and enqueues it on TARGET_MESSENGER.

   When TARGET_MESSENGER becomes unblocked, SEND_MESSENGER delivers
   its message to TARGET_MESSENGER.

   Resources are charged to SEND_ACTIVITY.

   If VG_IPC_SEND_ACTIVATE is set, an activation is sent to the thread
   associated with SEND_MESSENGER when SEND_MESSENGER's message is
   transferred to TARGET_MESSENGER (or, when TARGET_MESSENGER is
   destroyed).


   RETURN PHASE

   If FLAGS contains VG_IPC_RETURN, the IPC returns.  Otherwise, the
   calling thread is suspended until it is next activated.  */
static inline error_t
vg_ipc_full (uintptr_t flags,
	     addr_t recv_activity, addr_t recv_messenger, addr_t recv_buf,
	     addr_t recv_inline_cap,
	     addr_t send_activity, addr_t target_messenger,
	     addr_t send_messenger, addr_t send_buf,
	     uintptr_t send_inline_word1, uintptr_t send_inline_word2,
	     addr_t send_inline_cap)
{
  error_t err = 0;

#ifdef USE_L4
  l4_msg_tag_t tag = l4_niltag;
  l4_msg_tag_set_label (&tag, 8194);

  l4_msg_t msg;
  l4_msg_clear (msg);
  l4_msg_set_msg_tag (msg, tag);

  void msg_append_addr (addr_t addr)
  {
    int i;
    for (i = 0; i < sizeof (addr_t) / sizeof (uintptr_t); i ++)
      l4_msg_append_word (msg, ((uintptr_t *) &addr)[i]);
  }

  l4_msg_append_word (msg, flags);

  msg_append_addr (recv_activity);
  msg_append_addr (recv_messenger);
  msg_append_addr (recv_buf);
  msg_append_addr (recv_inline_cap);

  msg_append_addr (send_activity);
  msg_append_addr (target_messenger);

  msg_append_addr (send_messenger);
  msg_append_addr (send_buf);

  l4_msg_append_word (msg, send_inline_word1);
  l4_msg_append_word (msg, send_inline_word2);
  msg_append_addr (send_inline_cap);

  l4_msg_load (msg);
  l4_accept (l4_map_grant_items (L4_COMPLETE_ADDRESS_SPACE));

  bool call = true;

  while (1)
    {
      extern struct hurd_startup_data *__hurd_startup_data;      

      if (call)
	tag = l4_call (__hurd_startup_data->rm);
      else
	tag = l4_receive (__hurd_startup_data->rm);

      if (likely (l4_ipc_failed (tag)))
	{
	  if (((l4_error_code () >> 1) & 0x7) == 3)
	    {
	      if (l4_error_code () & 1)
		/* IPC was interrupted in the receive phase, i.e., we
		   got a response.  */
		break;
	      else
		call = false;
	    }
	  else
	    return EHOSTDOWN;
	}
      else
	{
	  assert (l4_untyped_words (tag) == 1);
	  l4_msg_store (tag, msg);
	  /* Potential error performing IPC (or VG_RETURN specified).  */
	  err = l4_msg_word (msg, 1);
	  break;
	}
    }
#else
# warning vg_ipc not ported to this architecture.
#endif

  return err;
}

static inline error_t
vg_ipc (uintptr_t flags,
	addr_t recv_activity, addr_t recv_messenger, addr_t recv_buf,
	addr_t send_activity, addr_t target_messenger,
	addr_t send_messenger, addr_t send_buf)
{
  return vg_ipc_full (flags,
		      recv_activity, recv_messenger, recv_buf, ADDR_VOID,
		      send_activity, target_messenger,
		      send_messenger, send_buf,
		      0, 0, ADDR_VOID);
}

static inline error_t
vg_ipc_short (uintptr_t flags,
	      addr_t recv_activity, addr_t recv_messenger, addr_t recv_cap,
	      addr_t send_activity, addr_t target_messenger,
	      addr_t send_messenger,
	      uintptr_t inline_word1, uintptr_t inline_word2,
	      addr_t inline_cap)
{
  return vg_ipc_full (flags, 
		      recv_activity, recv_messenger, ADDR_VOID, recv_cap,
		      send_activity, target_messenger,
		      send_messenger, ADDR_VOID,
		      inline_word1, inline_word2, inline_cap);
}

static inline error_t
vg_send (uintptr_t flags, addr_t send_activity, addr_t target_messenger,
	 addr_t send_messenger, addr_t send_buf)
{
  return vg_ipc_full (flags | VG_IPC_SEND | VG_IPC_SEND_ACTIVATE,
		      ADDR_VOID, ADDR_VOID, ADDR_VOID, ADDR_VOID,
		      send_activity, target_messenger,
		      send_messenger, send_buf,
		      0, 0, ADDR_VOID);
}

static inline error_t
vg_reply (uintptr_t flags, addr_t send_activity, addr_t target_messenger,
	  addr_t send_messenger, addr_t send_buf)
{
  return vg_ipc_full (flags | VG_IPC_SEND | VG_IPC_SEND_NONBLOCKING,
		      ADDR_VOID, ADDR_VOID, ADDR_VOID, ADDR_VOID,
		      send_activity, target_messenger, send_messenger, send_buf,
		      0, 0, ADDR_VOID);
}

/* Suspend the caller until the next activation.  */
static inline error_t
vg_suspend (void)
{
  return vg_ipc_full (0,
		      ADDR_VOID, ADDR_VOID, ADDR_VOID, ADDR_VOID,
		      ADDR_VOID, ADDR_VOID, ADDR_VOID, ADDR_VOID,
		      0, 0, ADDR_VOID);
}

#endif

#endif