~vojtech-horky/helenos/numa

« back to all changes in this revision

Viewing changes to kernel/generic/src/ipc/sysipc.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2006 Ondrej Palkovsky
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * - Redistributions of source code must retain the above copyright
 
10
 *   notice, this list of conditions and the following disclaimer.
 
11
 * - Redistributions in binary form must reproduce the above copyright
 
12
 *   notice, this list of conditions and the following disclaimer in the
 
13
 *   documentation and/or other materials provided with the distribution.
 
14
 * - The name of the author may not be used to endorse or promote products
 
15
 *   derived from this software without specific prior written permission.
 
16
 *
 
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
27
 */
 
28
 
 
29
/** @addtogroup genericipc
 
30
 * @{
 
31
 */
 
32
/** @file
 
33
 */
 
34
 
 
35
#include <arch.h>
 
36
#include <proc/task.h>
 
37
#include <proc/thread.h>
 
38
#include <errno.h>
 
39
#include <memstr.h>
 
40
#include <debug.h>
 
41
#include <ipc/ipc.h>
 
42
#include <ipc/sysipc.h>
 
43
#include <ipc/irq.h>
 
44
#include <ipc/ipcrsc.h>
 
45
#include <ipc/kbox.h>
 
46
#include <udebug/udebug_ipc.h>
 
47
#include <arch/interrupt.h>
 
48
#include <syscall/copy.h>
 
49
#include <security/cap.h>
 
50
#include <mm/as.h>
 
51
#include <print.h>
 
52
 
 
53
/**
 
54
 * Maximum buffer size allowed for IPC_M_DATA_WRITE and IPC_M_DATA_READ
 
55
 * requests.
 
56
 */
 
57
#define DATA_XFER_LIMIT         (64 * 1024)
 
58
 
 
59
#define GET_CHECK_PHONE(phone, phoneid, err) \
 
60
{ \
 
61
        if (phoneid > IPC_MAX_PHONES) { \
 
62
                err; \
 
63
        } \
 
64
        phone = &TASK->phones[phoneid]; \
 
65
}
 
66
 
 
67
#define STRUCT_TO_USPACE(dst, src)      copy_to_uspace(dst, src, sizeof(*(src)))
 
68
 
 
69
/** Decide if the method is a system method.
 
70
 *
 
71
 * @param method        Method to be decided.
 
72
 *
 
73
 * @return              Return 1 if the method is a system method.
 
74
 *                      Otherwise return 0.
 
75
 */
 
76
static inline int method_is_system(unative_t method)
 
77
{
 
78
        if (method <= IPC_M_LAST_SYSTEM)
 
79
                return 1;
 
80
        return 0;
 
81
}
 
82
 
 
83
/** Decide if the message with this method is forwardable.
 
84
 *
 
85
 * - some system messages may be forwarded, for some of them
 
86
 *   it is useless
 
87
 *
 
88
 * @param method        Method to be decided.
 
89
 *
 
90
 * @return              Return 1 if the method is forwardable.
 
91
 *                      Otherwise return 0.
 
92
 */
 
93
static inline int method_is_forwardable(unative_t method)
 
94
{
 
95
        switch (method) {
 
96
        case IPC_M_CONNECTION_CLONE:
 
97
        case IPC_M_CONNECT_ME:
 
98
        case IPC_M_PHONE_HUNGUP:
 
99
                /* This message is meant only for the original recipient. */
 
100
                return 0;
 
101
        default:
 
102
                return 1;
 
103
        }
 
104
}
 
105
 
 
106
/** Decide if the message with this method is immutable on forward.
 
107
 *
 
108
 * - some system messages may be forwarded but their content cannot be altered
 
109
 *
 
110
 * @param method        Method to be decided.
 
111
 *
 
112
 * @return              Return 1 if the method is immutable on forward.
 
113
 *                      Otherwise return 0.
 
114
 */
 
115
static inline int method_is_immutable(unative_t method)
 
116
{
 
117
        switch (method) {
 
118
        case IPC_M_SHARE_OUT:
 
119
        case IPC_M_SHARE_IN:
 
120
        case IPC_M_DATA_WRITE:
 
121
        case IPC_M_DATA_READ:
 
122
                return 1;
 
123
                break;
 
124
        default:
 
125
                return 0;
 
126
        }
 
127
}
 
128
 
 
129
 
 
130
/***********************************************************************
 
131
 * Functions that preprocess answer before sending it to the recepient.
 
132
 ***********************************************************************/
 
133
 
 
134
/** Decide if the caller (e.g. ipc_answer()) should save the old call contents
 
135
 * for answer_preprocess().
 
136
 *
 
137
 * @param call          Call structure to be decided.
 
138
 *
 
139
 * @return              Return 1 if the old call contents should be saved.
 
140
 *                      Return 0 otherwise.
 
141
 */
 
142
static inline int answer_need_old(call_t *call)
 
143
{
 
144
        switch (IPC_GET_METHOD(call->data)) {
 
145
        case IPC_M_CONNECTION_CLONE:
 
146
        case IPC_M_CONNECT_ME:
 
147
        case IPC_M_CONNECT_TO_ME:
 
148
        case IPC_M_CONNECT_ME_TO:
 
149
        case IPC_M_SHARE_OUT:
 
150
        case IPC_M_SHARE_IN:
 
151
        case IPC_M_DATA_WRITE:
 
152
        case IPC_M_DATA_READ:
 
153
                return 1;
 
154
        default:
 
155
                return 0;
 
156
        }
 
157
}
 
158
 
 
159
/** Interpret process answer as control information.
 
160
 *
 
161
 * This function is called directly after sys_ipc_answer().
 
162
 *
 
163
 * @param answer        Call structure with the answer.
 
164
 * @param olddata       Saved data of the request.
 
165
 *
 
166
 * @return              Return 0 on success or an error code. 
 
167
 */
 
168
static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
 
169
{
 
170
        int phoneid;
 
171
 
 
172
        if ((native_t) IPC_GET_RETVAL(answer->data) == EHANGUP) {
 
173
                /* In case of forward, hangup the forwared phone,
 
174
                 * not the originator
 
175
                 */
 
176
                mutex_lock(&answer->data.phone->lock);
 
177
                spinlock_lock(&TASK->answerbox.lock);
 
178
                if (answer->data.phone->state == IPC_PHONE_CONNECTED) {
 
179
                        list_remove(&answer->data.phone->link);
 
180
                        answer->data.phone->state = IPC_PHONE_SLAMMED;
 
181
                }
 
182
                spinlock_unlock(&TASK->answerbox.lock);
 
183
                mutex_unlock(&answer->data.phone->lock);
 
184
        }
 
185
 
 
186
        if (!olddata)
 
187
                return 0;
 
188
 
 
189
        if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTION_CLONE) {
 
190
                phoneid = IPC_GET_ARG1(*olddata);
 
191
                phone_t *phone = &TASK->phones[phoneid]; 
 
192
                if (IPC_GET_RETVAL(answer->data) != EOK) {
 
193
                        /*
 
194
                         * The recipient of the cloned phone rejected the offer.
 
195
                         * In this case, the connection was established at the
 
196
                         * request time and therefore we need to slam the phone.
 
197
                         * We don't merely hangup as that would result in
 
198
                         * sending IPC_M_HUNGUP to the third party on the
 
199
                         * other side of the cloned phone.
 
200
                         */
 
201
                        mutex_lock(&phone->lock);
 
202
                        if (phone->state == IPC_PHONE_CONNECTED) {
 
203
                                spinlock_lock(&phone->callee->lock);
 
204
                                list_remove(&phone->link);
 
205
                                phone->state = IPC_PHONE_SLAMMED;
 
206
                                spinlock_unlock(&phone->callee->lock);
 
207
                        }
 
208
                        mutex_unlock(&phone->lock);
 
209
                }
 
210
        } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME) {
 
211
                phone_t *phone = (phone_t *)IPC_GET_ARG5(*olddata);
 
212
                if (IPC_GET_RETVAL(answer->data) != EOK) {
 
213
                        /*
 
214
                         * The other party on the cloned phoned rejected our
 
215
                         * request for connection on the protocol level.
 
216
                         * We need to break the connection without sending
 
217
                         * IPC_M_HUNGUP back.
 
218
                         */
 
219
                        mutex_lock(&phone->lock);
 
220
                        if (phone->state == IPC_PHONE_CONNECTED) {
 
221
                                spinlock_lock(&phone->callee->lock);
 
222
                                list_remove(&phone->link);
 
223
                                phone->state = IPC_PHONE_SLAMMED;
 
224
                                spinlock_unlock(&phone->callee->lock);
 
225
                        }
 
226
                        mutex_unlock(&phone->lock);
 
227
                }
 
228
        } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
 
229
                phoneid = IPC_GET_ARG5(*olddata);
 
230
                if (IPC_GET_RETVAL(answer->data) != EOK) {
 
231
                        /* The connection was not accepted */
 
232
                        phone_dealloc(phoneid);
 
233
                } else {
 
234
                        /* The connection was accepted */
 
235
                        phone_connect(phoneid, &answer->sender->answerbox);
 
236
                        /* Set 'phone hash' as arg5 of response */
 
237
                        IPC_SET_ARG5(answer->data,
 
238
                            (unative_t) &TASK->phones[phoneid]);
 
239
                }
 
240
        } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
 
241
                /* If the users accepted call, connect */
 
242
                if (IPC_GET_RETVAL(answer->data) == EOK) {
 
243
                        ipc_phone_connect((phone_t *) IPC_GET_ARG5(*olddata),
 
244
                            &TASK->answerbox);
 
245
                }
 
246
        } else if (IPC_GET_METHOD(*olddata) == IPC_M_SHARE_OUT) {
 
247
                if (!IPC_GET_RETVAL(answer->data)) {
 
248
                        /* Accepted, handle as_area receipt */
 
249
                        ipl_t ipl;
 
250
                        int rc;
 
251
                        as_t *as;
 
252
                        
 
253
                        ipl = interrupts_disable();
 
254
                        spinlock_lock(&answer->sender->lock);
 
255
                        as = answer->sender->as;
 
256
                        spinlock_unlock(&answer->sender->lock);
 
257
                        interrupts_restore(ipl);
 
258
                        
 
259
                        rc = as_area_share(as, IPC_GET_ARG1(*olddata),
 
260
                            IPC_GET_ARG2(*olddata), AS,
 
261
                            IPC_GET_ARG1(answer->data), IPC_GET_ARG3(*olddata));
 
262
                        IPC_SET_RETVAL(answer->data, rc);
 
263
                        return rc;
 
264
                }
 
265
        } else if (IPC_GET_METHOD(*olddata) == IPC_M_SHARE_IN) {
 
266
                if (!IPC_GET_RETVAL(answer->data)) { 
 
267
                        ipl_t ipl;
 
268
                        as_t *as;
 
269
                        int rc;
 
270
                        
 
271
                        ipl = interrupts_disable();
 
272
                        spinlock_lock(&answer->sender->lock);
 
273
                        as = answer->sender->as;
 
274
                        spinlock_unlock(&answer->sender->lock);
 
275
                        interrupts_restore(ipl);
 
276
                        
 
277
                        rc = as_area_share(AS, IPC_GET_ARG1(answer->data),
 
278
                            IPC_GET_ARG2(*olddata), as, IPC_GET_ARG1(*olddata),
 
279
                            IPC_GET_ARG2(answer->data));
 
280
                        IPC_SET_RETVAL(answer->data, rc);
 
281
                }
 
282
        } else if (IPC_GET_METHOD(*olddata) == IPC_M_DATA_READ) {
 
283
                ASSERT(!answer->buffer);
 
284
                if (!IPC_GET_RETVAL(answer->data)) {
 
285
                        /* The recipient agreed to send data. */
 
286
                        uintptr_t src = IPC_GET_ARG1(answer->data);
 
287
                        uintptr_t dst = IPC_GET_ARG1(*olddata);
 
288
                        size_t max_size = IPC_GET_ARG2(*olddata);
 
289
                        size_t size = IPC_GET_ARG2(answer->data);
 
290
                        if (size && size <= max_size) {
 
291
                                /*
 
292
                                 * Copy the destination VA so that this piece of
 
293
                                 * information is not lost.
 
294
                                 */
 
295
                                IPC_SET_ARG1(answer->data, dst);
 
296
 
 
297
                                answer->buffer = malloc(size, 0);
 
298
                                int rc = copy_from_uspace(answer->buffer,
 
299
                                    (void *) src, size);
 
300
                                if (rc) {
 
301
                                        IPC_SET_RETVAL(answer->data, rc);
 
302
                                        free(answer->buffer);
 
303
                                        answer->buffer = NULL;
 
304
                                }
 
305
                        } else if (!size) {
 
306
                                IPC_SET_RETVAL(answer->data, EOK);
 
307
                        } else {
 
308
                                IPC_SET_RETVAL(answer->data, ELIMIT);
 
309
                        }
 
310
                }
 
311
        } else if (IPC_GET_METHOD(*olddata) == IPC_M_DATA_WRITE) {
 
312
                ASSERT(answer->buffer);
 
313
                if (!IPC_GET_RETVAL(answer->data)) {
 
314
                        /* The recipient agreed to receive data. */
 
315
                        int rc;
 
316
                        uintptr_t dst;
 
317
                        size_t size;
 
318
                        size_t max_size;
 
319
 
 
320
                        dst = (uintptr_t)IPC_GET_ARG1(answer->data);
 
321
                        size = (size_t)IPC_GET_ARG2(answer->data);
 
322
                        max_size = (size_t)IPC_GET_ARG2(*olddata);
 
323
 
 
324
                        if (size <= max_size) {
 
325
                                rc = copy_to_uspace((void *) dst,
 
326
                                    answer->buffer, size);
 
327
                                if (rc)
 
328
                                        IPC_SET_RETVAL(answer->data, rc);
 
329
                        } else {
 
330
                                IPC_SET_RETVAL(answer->data, ELIMIT);
 
331
                        }
 
332
                }
 
333
                free(answer->buffer);
 
334
                answer->buffer = NULL;
 
335
        }
 
336
        return 0;
 
337
}
 
338
 
 
339
static void phones_lock(phone_t *p1, phone_t *p2)
 
340
{
 
341
        if (p1 < p2) {
 
342
                mutex_lock(&p1->lock);
 
343
                mutex_lock(&p2->lock);
 
344
        } else if (p1 > p2) {
 
345
                mutex_lock(&p2->lock);
 
346
                mutex_lock(&p1->lock);
 
347
        } else {
 
348
                mutex_lock(&p1->lock);
 
349
        }
 
350
}
 
351
 
 
352
static void phones_unlock(phone_t *p1, phone_t *p2)
 
353
{
 
354
        mutex_unlock(&p1->lock);
 
355
        if (p1 != p2)
 
356
                mutex_unlock(&p2->lock);
 
357
}
 
358
 
 
359
/** Called before the request is sent.
 
360
 *
 
361
 * @param call          Call structure with the request.
 
362
 * @param phone         Phone that the call will be sent through.
 
363
 *
 
364
 * @return              Return 0 on success, ELIMIT or EPERM on error.
 
365
 */
 
366
static int request_preprocess(call_t *call, phone_t *phone)
 
367
{
 
368
        int newphid;
 
369
        size_t size;
 
370
        uintptr_t src;
 
371
        int rc;
 
372
 
 
373
        switch (IPC_GET_METHOD(call->data)) {
 
374
        case IPC_M_CONNECTION_CLONE: {
 
375
                phone_t *cloned_phone;
 
376
                GET_CHECK_PHONE(cloned_phone, IPC_GET_ARG1(call->data),
 
377
                    return ENOENT);
 
378
                phones_lock(cloned_phone, phone);
 
379
                if ((cloned_phone->state != IPC_PHONE_CONNECTED) ||
 
380
                    phone->state != IPC_PHONE_CONNECTED) {
 
381
                        phones_unlock(cloned_phone, phone);
 
382
                        return EINVAL;
 
383
                }
 
384
                /*
 
385
                 * We can be pretty sure now that both tasks exist and we are
 
386
                 * connected to them. As we continue to hold the phone locks,
 
387
                 * we are effectively preventing them from finishing their
 
388
                 * potential cleanup.
 
389
                 */
 
390
                newphid = phone_alloc(phone->callee->task);
 
391
                if (newphid < 0) {
 
392
                        phones_unlock(cloned_phone, phone);
 
393
                        return ELIMIT;
 
394
                }
 
395
                ipc_phone_connect(&phone->callee->task->phones[newphid],
 
396
                    cloned_phone->callee);
 
397
                phones_unlock(cloned_phone, phone);
 
398
                /* Set the new phone for the callee. */
 
399
                IPC_SET_ARG1(call->data, newphid);
 
400
                break;
 
401
        }
 
402
        case IPC_M_CONNECT_ME:
 
403
                IPC_SET_ARG5(call->data, (unative_t) phone);
 
404
                break;
 
405
        case IPC_M_CONNECT_ME_TO:
 
406
                newphid = phone_alloc(TASK);
 
407
                if (newphid < 0)
 
408
                        return ELIMIT;
 
409
                /* Set arg5 for server */
 
410
                IPC_SET_ARG5(call->data, (unative_t) &TASK->phones[newphid]);
 
411
                call->flags |= IPC_CALL_CONN_ME_TO;
 
412
                call->priv = newphid;
 
413
                break;
 
414
        case IPC_M_SHARE_OUT:
 
415
                size = as_area_get_size(IPC_GET_ARG1(call->data));
 
416
                if (!size)
 
417
                        return EPERM;
 
418
                IPC_SET_ARG2(call->data, size);
 
419
                break;
 
420
        case IPC_M_DATA_READ:
 
421
                size = IPC_GET_ARG2(call->data);
 
422
                if ((size <= 0 || (size > DATA_XFER_LIMIT)))
 
423
                        return ELIMIT;
 
424
                break;
 
425
        case IPC_M_DATA_WRITE:
 
426
                src = IPC_GET_ARG1(call->data);
 
427
                size = IPC_GET_ARG2(call->data);
 
428
                
 
429
                if (size > DATA_XFER_LIMIT)
 
430
                        return ELIMIT;
 
431
                
 
432
                call->buffer = (uint8_t *) malloc(size, 0);
 
433
                rc = copy_from_uspace(call->buffer, (void *) src, size);
 
434
                if (rc != 0) {
 
435
                        free(call->buffer);
 
436
                        return rc;
 
437
                }
 
438
                break;
 
439
#ifdef CONFIG_UDEBUG
 
440
        case IPC_M_DEBUG_ALL:
 
441
                return udebug_request_preprocess(call, phone);
 
442
#endif
 
443
        default:
 
444
                break;
 
445
        }
 
446
        return 0;
 
447
}
 
448
 
 
449
/*******************************************************************************
 
450
 * Functions called to process received call/answer before passing it to uspace.
 
451
 *******************************************************************************/
 
452
 
 
453
/** Do basic kernel processing of received call answer.
 
454
 *
 
455
 * @param call          Call structure with the answer.
 
456
 */
 
457
static void process_answer(call_t *call)
 
458
{
 
459
        if (((native_t) IPC_GET_RETVAL(call->data) == EHANGUP) &&
 
460
            (call->flags & IPC_CALL_FORWARDED))
 
461
                IPC_SET_RETVAL(call->data, EFORWARD);
 
462
 
 
463
        if (call->flags & IPC_CALL_CONN_ME_TO) {
 
464
                if (IPC_GET_RETVAL(call->data))
 
465
                        phone_dealloc(call->priv);
 
466
                else
 
467
                        IPC_SET_ARG5(call->data, call->priv);
 
468
        }
 
469
 
 
470
        if (call->buffer) {
 
471
                /* This must be an affirmative answer to IPC_M_DATA_READ. */
 
472
                /* or IPC_M_DEBUG_ALL/UDEBUG_M_MEM_READ... */
 
473
                uintptr_t dst = IPC_GET_ARG1(call->data);
 
474
                size_t size = IPC_GET_ARG2(call->data);
 
475
                int rc = copy_to_uspace((void *) dst, call->buffer, size);
 
476
                if (rc)
 
477
                        IPC_SET_RETVAL(call->data, rc);
 
478
                free(call->buffer);
 
479
                call->buffer = NULL;
 
480
        }
 
481
}
 
482
 
 
483
/** Do basic kernel processing of received call request.
 
484
 *
 
485
 * @param box           Destination answerbox structure.
 
486
 * @param call          Call structure with the request.
 
487
 *
 
488
 * @return              Return 0 if the call should be passed to userspace.
 
489
 *                      Return -1 if the call should be ignored.
 
490
 */
 
491
static int process_request(answerbox_t *box, call_t *call)
 
492
{
 
493
        int phoneid;
 
494
 
 
495
        if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
 
496
                phoneid = phone_alloc(TASK);
 
497
                if (phoneid < 0) { /* Failed to allocate phone */
 
498
                        IPC_SET_RETVAL(call->data, ELIMIT);
 
499
                        ipc_answer(box, call);
 
500
                        return -1;
 
501
                }
 
502
                IPC_SET_ARG5(call->data, phoneid);
 
503
        }
 
504
        switch (IPC_GET_METHOD(call->data)) {
 
505
        case IPC_M_DEBUG_ALL:
 
506
                return -1;
 
507
        default:
 
508
                break;
 
509
        }
 
510
        return 0;
 
511
}
 
512
 
 
513
/** Make a fast call over IPC, wait for reply and return to user.
 
514
 *
 
515
 * This function can handle only three arguments of payload, but is faster than
 
516
 * the generic function (i.e. sys_ipc_call_sync_slow()).
 
517
 *
 
518
 * @param phoneid       Phone handle for the call.
 
519
 * @param method        Method of the call.
 
520
 * @param arg1          Service-defined payload argument.
 
521
 * @param arg2          Service-defined payload argument.
 
522
 * @param arg3          Service-defined payload argument.
 
523
 * @param data          Address of userspace structure where the reply call will
 
524
 *                      be stored.
 
525
 *
 
526
 * @return              Returns 0 on success.
 
527
 *                      Return ENOENT if there is no such phone handle.
 
528
 */
 
529
unative_t sys_ipc_call_sync_fast(unative_t phoneid, unative_t method,
 
530
    unative_t arg1, unative_t arg2, unative_t arg3, ipc_data_t *data)
 
531
{
 
532
        call_t call;
 
533
        phone_t *phone;
 
534
        int res;
 
535
        int rc;
 
536
        
 
537
        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
538
 
 
539
        ipc_call_static_init(&call);
 
540
        IPC_SET_METHOD(call.data, method);
 
541
        IPC_SET_ARG1(call.data, arg1);
 
542
        IPC_SET_ARG2(call.data, arg2);
 
543
        IPC_SET_ARG3(call.data, arg3);
 
544
        /*
 
545
         * To achieve deterministic behavior, zero out arguments that are beyond
 
546
         * the limits of the fast version.
 
547
         */
 
548
        IPC_SET_ARG4(call.data, 0);
 
549
        IPC_SET_ARG5(call.data, 0);
 
550
 
 
551
        if (!(res = request_preprocess(&call, phone))) {
 
552
#ifdef CONFIG_UDEBUG
 
553
                udebug_stoppable_begin();
 
554
#endif
 
555
                rc = ipc_call_sync(phone, &call);
 
556
#ifdef CONFIG_UDEBUG
 
557
                udebug_stoppable_end();
 
558
#endif
 
559
                if (rc != EOK)
 
560
                        return rc;
 
561
                process_answer(&call);
 
562
 
 
563
        } else {
 
564
                IPC_SET_RETVAL(call.data, res);
 
565
        }
 
566
        rc = STRUCT_TO_USPACE(&data->args, &call.data.args);
 
567
        if (rc != 0)
 
568
                return rc;
 
569
 
 
570
        return 0;
 
571
}
 
572
 
 
573
/** Make a synchronous IPC call allowing to transmit the entire payload.
 
574
 *
 
575
 * @param phoneid       Phone handle for the call.
 
576
 * @param question      Userspace address of call data with the request.
 
577
 * @param reply         Userspace address of call data where to store the
 
578
 *                      answer.
 
579
 *
 
580
 * @return              Zero on success or an error code.
 
581
 */
 
582
unative_t sys_ipc_call_sync_slow(unative_t phoneid, ipc_data_t *question,
 
583
    ipc_data_t *reply)
 
584
{
 
585
        call_t call;
 
586
        phone_t *phone;
 
587
        int res;
 
588
        int rc;
 
589
 
 
590
        ipc_call_static_init(&call);
 
591
        rc = copy_from_uspace(&call.data.args, &question->args,
 
592
            sizeof(call.data.args));
 
593
        if (rc != 0)
 
594
                return (unative_t) rc;
 
595
 
 
596
        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
597
 
 
598
        if (!(res = request_preprocess(&call, phone))) {
 
599
#ifdef CONFIG_UDEBUG
 
600
                udebug_stoppable_begin();
 
601
#endif
 
602
                rc = ipc_call_sync(phone, &call);
 
603
#ifdef CONFIG_UDEBUG
 
604
                udebug_stoppable_end();
 
605
#endif
 
606
                if (rc != EOK)
 
607
                        return rc;
 
608
                process_answer(&call);
 
609
        } else 
 
610
                IPC_SET_RETVAL(call.data, res);
 
611
 
 
612
        rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
 
613
        if (rc != 0)
 
614
                return rc;
 
615
 
 
616
        return 0;
 
617
}
 
618
 
 
619
/** Check that the task did not exceed the allowed limit of asynchronous calls.
 
620
 *
 
621
 * @return              Return 0 if limit not reached or -1 if limit exceeded.
 
622
 */
 
623
static int check_call_limit(void)
 
624
{
 
625
        if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
 
626
                atomic_dec(&TASK->active_calls);
 
627
                return -1;
 
628
        }
 
629
        return 0;
 
630
}
 
631
 
 
632
/** Make a fast asynchronous call over IPC.
 
633
 *
 
634
 * This function can only handle four arguments of payload, but is faster than
 
635
 * the generic function sys_ipc_call_async_slow().
 
636
 *
 
637
 * @param phoneid       Phone handle for the call.
 
638
 * @param method        Method of the call.
 
639
 * @param arg1          Service-defined payload argument.
 
640
 * @param arg2          Service-defined payload argument.
 
641
 * @param arg3          Service-defined payload argument.
 
642
 * @param arg4          Service-defined payload argument.
 
643
 *
 
644
 * @return              Return call hash on success.
 
645
 *                      Return IPC_CALLRET_FATAL in case of a fatal error and 
 
646
 *                      IPC_CALLRET_TEMPORARY if there are too many pending
 
647
 *                      asynchronous requests; answers should be handled first.
 
648
 */
 
649
unative_t sys_ipc_call_async_fast(unative_t phoneid, unative_t method,
 
650
    unative_t arg1, unative_t arg2, unative_t arg3, unative_t arg4)
 
651
{
 
652
        call_t *call;
 
653
        phone_t *phone;
 
654
        int res;
 
655
 
 
656
        if (check_call_limit())
 
657
                return IPC_CALLRET_TEMPORARY;
 
658
 
 
659
        GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
 
660
 
 
661
        call = ipc_call_alloc(0);
 
662
        IPC_SET_METHOD(call->data, method);
 
663
        IPC_SET_ARG1(call->data, arg1);
 
664
        IPC_SET_ARG2(call->data, arg2);
 
665
        IPC_SET_ARG3(call->data, arg3);
 
666
        IPC_SET_ARG4(call->data, arg4);
 
667
        /*
 
668
         * To achieve deterministic behavior, zero out arguments that are beyond
 
669
         * the limits of the fast version.
 
670
         */
 
671
        IPC_SET_ARG5(call->data, 0);
 
672
 
 
673
        if (!(res = request_preprocess(call, phone)))
 
674
                ipc_call(phone, call);
 
675
        else
 
676
                ipc_backsend_err(phone, call, res);
 
677
 
 
678
        return (unative_t) call;
 
679
}
 
680
 
 
681
/** Make an asynchronous IPC call allowing to transmit the entire payload.
 
682
 *
 
683
 * @param phoneid       Phone handle for the call.
 
684
 * @param data          Userspace address of call data with the request.
 
685
 *
 
686
 * @return              See sys_ipc_call_async_fast(). 
 
687
 */
 
688
unative_t sys_ipc_call_async_slow(unative_t phoneid, ipc_data_t *data)
 
689
{
 
690
        call_t *call;
 
691
        phone_t *phone;
 
692
        int res;
 
693
        int rc;
 
694
 
 
695
        if (check_call_limit())
 
696
                return IPC_CALLRET_TEMPORARY;
 
697
 
 
698
        GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
 
699
 
 
700
        call = ipc_call_alloc(0);
 
701
        rc = copy_from_uspace(&call->data.args, &data->args,
 
702
            sizeof(call->data.args));
 
703
        if (rc != 0) {
 
704
                ipc_call_free(call);
 
705
                return (unative_t) rc;
 
706
        }
 
707
        if (!(res = request_preprocess(call, phone)))
 
708
                ipc_call(phone, call);
 
709
        else
 
710
                ipc_backsend_err(phone, call, res);
 
711
 
 
712
        return (unative_t) call;
 
713
}
 
714
 
 
715
/** Forward a received call to another destination - common code for both the
 
716
 * fast and the slow version.
 
717
 *
 
718
 * @param callid        Hash of the call to forward.
 
719
 * @param phoneid       Phone handle to use for forwarding.
 
720
 * @param method        New method to use for the forwarded call.
 
721
 * @param arg1          New value of the first argument for the forwarded call.
 
722
 * @param arg2          New value of the second argument for the forwarded call.
 
723
 * @param arg3          New value of the third argument for the forwarded call.
 
724
 * @param arg4          New value of the fourth argument for the forwarded call.
 
725
 * @param arg5          New value of the fifth argument for the forwarded call.
 
726
 * @param mode          Flags that specify mode of the forward operation.
 
727
 * @param slow          If true, arg3, arg4 and arg5 are considered. Otherwise
 
728
 *                      the function considers only the fast version arguments:
 
729
 *                      i.e. arg1 and arg2.
 
730
 *
 
731
 * @return              Return 0 on succes, otherwise return an error code.
 
732
 *
 
733
 * Warning:     Make sure that ARG5 is not rewritten for certain system IPC
 
734
 */
 
735
static unative_t sys_ipc_forward_common(unative_t callid, unative_t phoneid,
 
736
    unative_t method, unative_t arg1, unative_t arg2, unative_t arg3,
 
737
    unative_t arg4, unative_t arg5, int mode, bool slow)
 
738
{
 
739
        call_t *call;
 
740
        phone_t *phone;
 
741
 
 
742
        call = get_call(callid);
 
743
        if (!call)
 
744
                return ENOENT;
 
745
        
 
746
        call->flags |= IPC_CALL_FORWARDED;
 
747
 
 
748
        GET_CHECK_PHONE(phone, phoneid, { 
 
749
                IPC_SET_RETVAL(call->data, EFORWARD);
 
750
                ipc_answer(&TASK->answerbox, call);
 
751
                return ENOENT;
 
752
        });
 
753
 
 
754
        if (!method_is_forwardable(IPC_GET_METHOD(call->data))) {
 
755
                IPC_SET_RETVAL(call->data, EFORWARD);
 
756
                ipc_answer(&TASK->answerbox, call);
 
757
                return EPERM;
 
758
        }
 
759
 
 
760
        /*
 
761
         * Userspace is not allowed to change method of system methods on
 
762
         * forward, allow changing ARG1, ARG2, ARG3 and ARG4 by means of method,
 
763
         * arg1, arg2 and arg3.
 
764
         * If the method is immutable, don't change anything.
 
765
         */
 
766
        if (!method_is_immutable(IPC_GET_METHOD(call->data))) {
 
767
                if (method_is_system(IPC_GET_METHOD(call->data))) {
 
768
                        if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
 
769
                                phone_dealloc(IPC_GET_ARG5(call->data));
 
770
 
 
771
                        IPC_SET_ARG1(call->data, method);
 
772
                        IPC_SET_ARG2(call->data, arg1);
 
773
                        IPC_SET_ARG3(call->data, arg2);
 
774
                        if (slow) {
 
775
                                IPC_SET_ARG4(call->data, arg3);
 
776
                                /*
 
777
                                 * For system methods we deliberately don't
 
778
                                 * overwrite ARG5.
 
779
                                 */
 
780
                        }
 
781
                } else {
 
782
                        IPC_SET_METHOD(call->data, method);
 
783
                        IPC_SET_ARG1(call->data, arg1);
 
784
                        IPC_SET_ARG2(call->data, arg2);
 
785
                        if (slow) {
 
786
                                IPC_SET_ARG3(call->data, arg3);
 
787
                                IPC_SET_ARG4(call->data, arg4);
 
788
                                IPC_SET_ARG5(call->data, arg5);
 
789
                        }
 
790
                }
 
791
        }
 
792
 
 
793
        return ipc_forward(call, phone, &TASK->answerbox, mode);
 
794
}
 
795
 
 
796
/** Forward a received call to another destination - fast version.
 
797
 *
 
798
 * @param callid        Hash of the call to forward.
 
799
 * @param phoneid       Phone handle to use for forwarding.
 
800
 * @param method        New method to use for the forwarded call.
 
801
 * @param arg1          New value of the first argument for the forwarded call.
 
802
 * @param arg2          New value of the second argument for the forwarded call.
 
803
 * @param mode          Flags that specify mode of the forward operation.
 
804
 *
 
805
 * @return              Return 0 on succes, otherwise return an error code.
 
806
 *
 
807
 * In case the original method is a system method, ARG1, ARG2 and ARG3 are
 
808
 * overwritten in the forwarded message with the new method and the new
 
809
 * arg1 and arg2, respectively. Otherwise the METHOD, ARG1 and ARG2 are
 
810
 * rewritten with the new method, arg1 and arg2, respectively. Also note there
 
811
 * is a set of immutable methods, for which the new method and arguments are not
 
812
 * set and these values are ignored.
 
813
 */
 
814
unative_t sys_ipc_forward_fast(unative_t callid, unative_t phoneid,
 
815
    unative_t method, unative_t arg1, unative_t arg2, int mode)
 
816
{
 
817
        return sys_ipc_forward_common(callid, phoneid, method, arg1, arg2, 0, 0,
 
818
            0, mode, false); 
 
819
}
 
820
 
 
821
/** Forward a received call to another destination - slow version.
 
822
 *
 
823
 * @param callid        Hash of the call to forward.
 
824
 * @param phoneid       Phone handle to use for forwarding.
 
825
 * @param data          Userspace address of the new IPC data. 
 
826
 * @param mode          Flags that specify mode of the forward operation.
 
827
 *
 
828
 * @return              Return 0 on succes, otherwise return an error code.
 
829
 *
 
830
 * This function is the slow verision of the sys_ipc_forward_fast interface.
 
831
 * It can copy all five new arguments and the new method from the userspace.
 
832
 * It naturally extends the functionality of the fast version. For system
 
833
 * methods, it additionally stores the new value of arg3 to ARG4. For non-system
 
834
 * methods, it additionally stores the new value of arg3, arg4 and arg5,
 
835
 * respectively, to ARG3, ARG4 and ARG5, respectively.
 
836
 */
 
837
unative_t sys_ipc_forward_slow(unative_t callid, unative_t phoneid,
 
838
    ipc_data_t *data, int mode)
 
839
{
 
840
        ipc_data_t newdata;
 
841
        int rc;
 
842
 
 
843
        rc = copy_from_uspace(&newdata.args, &data->args,
 
844
            sizeof(newdata.args));
 
845
        if (rc != 0) 
 
846
                return (unative_t) rc;
 
847
 
 
848
        return sys_ipc_forward_common(callid, phoneid,
 
849
            IPC_GET_METHOD(newdata), IPC_GET_ARG1(newdata),
 
850
            IPC_GET_ARG2(newdata), IPC_GET_ARG3(newdata),
 
851
            IPC_GET_ARG4(newdata), IPC_GET_ARG5(newdata), mode, true); 
 
852
}
 
853
 
 
854
/** Answer an IPC call - fast version.
 
855
 *
 
856
 * This function can handle only two return arguments of payload, but is faster
 
857
 * than the generic sys_ipc_answer().
 
858
 *
 
859
 * @param callid        Hash of the call to be answered.
 
860
 * @param retval        Return value of the answer.
 
861
 * @param arg1          Service-defined return value.
 
862
 * @param arg2          Service-defined return value.
 
863
 * @param arg3          Service-defined return value.
 
864
 * @param arg4          Service-defined return value.
 
865
 *
 
866
 * @return              Return 0 on success, otherwise return an error code.    
 
867
 */
 
868
unative_t sys_ipc_answer_fast(unative_t callid, unative_t retval,
 
869
    unative_t arg1, unative_t arg2, unative_t arg3, unative_t arg4)
 
870
{
 
871
        call_t *call;
 
872
        ipc_data_t saved_data;
 
873
        int saveddata = 0;
 
874
        int rc;
 
875
 
 
876
        /* Do not answer notification callids */
 
877
        if (callid & IPC_CALLID_NOTIFICATION)
 
878
                return 0;
 
879
 
 
880
        call = get_call(callid);
 
881
        if (!call)
 
882
                return ENOENT;
 
883
 
 
884
        if (answer_need_old(call)) {
 
885
                memcpy(&saved_data, &call->data, sizeof(call->data));
 
886
                saveddata = 1;
 
887
        }
 
888
 
 
889
        IPC_SET_RETVAL(call->data, retval);
 
890
        IPC_SET_ARG1(call->data, arg1);
 
891
        IPC_SET_ARG2(call->data, arg2);
 
892
        IPC_SET_ARG3(call->data, arg3);
 
893
        IPC_SET_ARG4(call->data, arg4);
 
894
        /*
 
895
         * To achieve deterministic behavior, zero out arguments that are beyond
 
896
         * the limits of the fast version.
 
897
         */
 
898
        IPC_SET_ARG5(call->data, 0);
 
899
        rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
 
900
 
 
901
        ipc_answer(&TASK->answerbox, call);
 
902
        return rc;
 
903
}
 
904
 
 
905
/** Answer an IPC call.
 
906
 *
 
907
 * @param callid        Hash of the call to be answered.
 
908
 * @param data          Userspace address of call data with the answer.
 
909
 *
 
910
 * @return              Return 0 on success, otherwise return an error code.
 
911
 */
 
912
unative_t sys_ipc_answer_slow(unative_t callid, ipc_data_t *data)
 
913
{
 
914
        call_t *call;
 
915
        ipc_data_t saved_data;
 
916
        int saveddata = 0;
 
917
        int rc;
 
918
 
 
919
        /* Do not answer notification callids */
 
920
        if (callid & IPC_CALLID_NOTIFICATION)
 
921
                return 0;
 
922
 
 
923
        call = get_call(callid);
 
924
        if (!call)
 
925
                return ENOENT;
 
926
 
 
927
        if (answer_need_old(call)) {
 
928
                memcpy(&saved_data, &call->data, sizeof(call->data));
 
929
                saveddata = 1;
 
930
        }
 
931
        rc = copy_from_uspace(&call->data.args, &data->args, 
 
932
            sizeof(call->data.args));
 
933
        if (rc != 0)
 
934
                return rc;
 
935
 
 
936
        rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
 
937
        
 
938
        ipc_answer(&TASK->answerbox, call);
 
939
 
 
940
        return rc;
 
941
}
 
942
 
 
943
/** Hang up a phone.
 
944
 *
 
945
 * @param               Phone handle of the phone to be hung up.
 
946
 *
 
947
 * @return              Return 0 on success or an error code.
 
948
 */
 
949
unative_t sys_ipc_hangup(int phoneid)
 
950
{
 
951
        phone_t *phone;
 
952
 
 
953
        GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
954
 
 
955
        if (ipc_phone_hangup(phone))
 
956
                return -1;
 
957
 
 
958
        return 0;
 
959
}
 
960
 
 
961
/** Wait for an incoming IPC call or an answer.
 
962
 *
 
963
 * @param calldata      Pointer to buffer where the call/answer data is stored.
 
964
 * @param usec          Timeout. See waitq_sleep_timeout() for explanation.
 
965
 * @param flags         Select mode of sleep operation. See waitq_sleep_timeout()
 
966
 *                      for explanation.
 
967
 *
 
968
 * @return              Hash of the call.
 
969
 *                      If IPC_CALLID_NOTIFICATION bit is set in the hash, the
 
970
 *                      call is a notification. IPC_CALLID_ANSWERED denotes an
 
971
 *                      answer.
 
972
 */
 
973
unative_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec, int flags)
 
974
{
 
975
        call_t *call;
 
976
 
 
977
restart:
 
978
 
 
979
#ifdef CONFIG_UDEBUG
 
980
        udebug_stoppable_begin();
 
981
#endif  
 
982
        call = ipc_wait_for_call(&TASK->answerbox, usec,
 
983
            flags | SYNCH_FLAGS_INTERRUPTIBLE);
 
984
 
 
985
#ifdef CONFIG_UDEBUG
 
986
        udebug_stoppable_end();
 
987
#endif
 
988
        if (!call)
 
989
                return 0;
 
990
 
 
991
        if (call->flags & IPC_CALL_NOTIF) {
 
992
                ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
 
993
 
 
994
                /* Set in_phone_hash to the interrupt counter */
 
995
                call->data.phone = (void *) call->priv;
 
996
                
 
997
                STRUCT_TO_USPACE(calldata, &call->data);
 
998
 
 
999
                ipc_call_free(call);
 
1000
                
 
1001
                return ((unative_t) call) | IPC_CALLID_NOTIFICATION;
 
1002
        }
 
1003
 
 
1004
        if (call->flags & IPC_CALL_ANSWERED) {
 
1005
                process_answer(call);
 
1006
 
 
1007
                ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
 
1008
 
 
1009
                if (call->flags & IPC_CALL_DISCARD_ANSWER) {
 
1010
                        ipc_call_free(call);
 
1011
                        goto restart;
 
1012
                } else {
 
1013
                        /*
 
1014
                         * Decrement the counter of active calls only if the
 
1015
                         * call is not an answer to IPC_M_PHONE_HUNGUP,
 
1016
                         * which doesn't contribute to the counter.
 
1017
                         */
 
1018
                        atomic_dec(&TASK->active_calls);
 
1019
                }
 
1020
 
 
1021
                STRUCT_TO_USPACE(&calldata->args, &call->data.args);
 
1022
                ipc_call_free(call);
 
1023
 
 
1024
                return ((unative_t) call) | IPC_CALLID_ANSWERED;
 
1025
        }
 
1026
 
 
1027
        if (process_request(&TASK->answerbox, call))
 
1028
                goto restart;
 
1029
 
 
1030
        /* Include phone address('id') of the caller in the request,
 
1031
         * copy whole call->data, not only call->data.args */
 
1032
        if (STRUCT_TO_USPACE(calldata, &call->data)) {
 
1033
                /*
 
1034
                 * The callee will not receive this call and no one else has
 
1035
                 * a chance to answer it. Reply with the EPARTY error code.
 
1036
                 */
 
1037
                ipc_data_t saved_data;
 
1038
                int saveddata = 0;
 
1039
 
 
1040
                if (answer_need_old(call)) {
 
1041
                        memcpy(&saved_data, &call->data, sizeof(call->data));
 
1042
                        saveddata = 1;
 
1043
                }
 
1044
                
 
1045
                IPC_SET_RETVAL(call->data, EPARTY);
 
1046
                (void) answer_preprocess(call, saveddata ? &saved_data : NULL);
 
1047
                ipc_answer(&TASK->answerbox, call);
 
1048
                return 0;
 
1049
        }
 
1050
        return (unative_t)call;
 
1051
}
 
1052
 
 
1053
/** Connect an IRQ handler to a task.
 
1054
 *
 
1055
 * @param inr           IRQ number.
 
1056
 * @param devno         Device number.
 
1057
 * @param method        Method to be associated with the notification.
 
1058
 * @param ucode         Uspace pointer to the top-half pseudocode.
 
1059
 *
 
1060
 * @return              EPERM or a return code returned by ipc_irq_register().
 
1061
 */
 
1062
unative_t sys_ipc_register_irq(inr_t inr, devno_t devno, unative_t method,
 
1063
    irq_code_t *ucode)
 
1064
{
 
1065
        if (!(cap_get(TASK) & CAP_IRQ_REG))
 
1066
                return EPERM;
 
1067
 
 
1068
        return ipc_irq_register(&TASK->answerbox, inr, devno, method, ucode);
 
1069
}
 
1070
 
 
1071
/** Disconnect an IRQ handler from a task.
 
1072
 *
 
1073
 * @param inr           IRQ number.
 
1074
 * @param devno         Device number.
 
1075
 *
 
1076
 * @return              Zero on success or EPERM on error..
 
1077
 */
 
1078
unative_t sys_ipc_unregister_irq(inr_t inr, devno_t devno)
 
1079
{
 
1080
        if (!(cap_get(TASK) & CAP_IRQ_REG))
 
1081
                return EPERM;
 
1082
 
 
1083
        ipc_irq_unregister(&TASK->answerbox, inr, devno);
 
1084
 
 
1085
        return 0;
 
1086
}
 
1087
 
 
1088
#include <console/console.h>
 
1089
 
 
1090
/**
 
1091
 * Syscall connect to a task by id.
 
1092
 *
 
1093
 * @return              Phone id on success, or negative error code.
 
1094
 */
 
1095
unative_t sys_ipc_connect_kbox(sysarg64_t *uspace_taskid_arg)
 
1096
{
 
1097
#ifdef CONFIG_UDEBUG
 
1098
        sysarg64_t taskid_arg;
 
1099
        int rc;
 
1100
        
 
1101
        rc = copy_from_uspace(&taskid_arg, uspace_taskid_arg, sizeof(sysarg64_t));
 
1102
        if (rc != 0)
 
1103
                return (unative_t) rc;
 
1104
 
 
1105
        LOG("sys_ipc_connect_kbox(%" PRIu64 ")\n", taskid_arg.value);
 
1106
 
 
1107
        return ipc_connect_kbox(taskid_arg.value);
 
1108
#else
 
1109
        return (unative_t) ENOTSUP;
 
1110
#endif
 
1111
}
 
1112
 
 
1113
/** @}
 
1114
 */