Subversion Repositories HelenOS-historic

Rev

Rev 1040 | Rev 1063 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1027 palkovsky 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
#include <arch.h>
30
#include <proc/task.h>
31
 
32
#include <errno.h>
33
#include <mm/page.h>
34
#include <memstr.h>
35
#include <debug.h>
36
#include <ipc/ipc.h>
37
#include <ipc/sysipc.h>
1060 palkovsky 38
 
39
 
1027 palkovsky 40
#include <print.h>
1060 palkovsky 41
#include <arch.h>
42
#include <proc/thread.h>
1027 palkovsky 43
 
44
/* TODO: multi-threaded connect-to-me can cause race condition
45
 * on phone, add counter + thread_kill??
46
 *
47
 */
48
 
1040 palkovsky 49
/** Return true if the method is a system method */
50
static inline int is_system_method(__native method)
51
{
52
    if (method <= IPC_M_LAST_SYSTEM)
53
        return 1;
54
    return 0;
55
}
56
 
57
/** Return true if the message with this method is forwardable
58
 *
59
 * - some system messages may be forwarded, for some of them
60
 *   it is useless
61
 */
62
static inline int is_forwardable(__native method)
63
{
64
    return 1;
65
}
66
 
67
/** Find call_t * in call table according to callid
68
 *
69
 * @return NULL on not found, otherwise pointer to call structure
70
 */
71
static inline call_t * get_call(__native callid)
72
{
73
    /* TODO: Traverse list of dispatched calls and find one */
74
    /* TODO: locking of call, ripping it from dispatched calls etc.  */
75
    return (call_t *) callid;
76
}
77
 
1027 palkovsky 78
/** Return pointer to phone identified by phoneid or NULL if non-existent */
79
static phone_t * get_phone(__native phoneid)
80
{
81
    phone_t *phone;
82
 
83
    if (phoneid >= IPC_MAX_PHONES)
84
        return NULL;
85
 
86
    phone = &TASK->phones[phoneid];
87
    if (!phone->callee)
88
        return NULL;
89
    return phone;
90
}
91
 
92
/** Allocate new phone slot in current TASK structure */
1040 palkovsky 93
static int phone_alloc(void)
1027 palkovsky 94
{
95
    int i;
96
 
97
    spinlock_lock(&TASK->lock);
98
 
99
    for (i=0; i < IPC_MAX_PHONES; i++) {
1040 palkovsky 100
        if (!TASK->phones[i].busy) {
101
            TASK->phones[i].busy = 1;
1027 palkovsky 102
            break;
103
        }
104
    }
105
    spinlock_unlock(&TASK->lock);
106
 
107
    if (i >= IPC_MAX_PHONES)
108
        return -1;
109
    return i;
110
}
111
 
112
/** Disconnect phone */
113
static void phone_dealloc(int phoneid)
114
{
1029 palkovsky 115
    spinlock_lock(&TASK->lock);
1027 palkovsky 116
 
1040 palkovsky 117
    ASSERT(TASK->phones[phoneid].busy);
1027 palkovsky 118
 
1040 palkovsky 119
    if (TASK->phones[phoneid].callee)
120
        ipc_phone_destroy(&TASK->phones[phoneid]);
121
 
122
    TASK->phones[phoneid].busy = 0;
1029 palkovsky 123
    spinlock_unlock(&TASK->lock);
1027 palkovsky 124
}
125
 
1040 palkovsky 126
static void phone_connect(int phoneid, answerbox_t *box)
127
{
128
    phone_t *phone = &TASK->phones[phoneid];
129
 
130
    ipc_phone_connect(phone, box);
131
}
132
 
1027 palkovsky 133
/****************************************************/
134
/* Functions that preprocess answer before sending
135
 * it to the recepient
136
 */
137
 
138
/** Return true if the caller (ipc_answer) should save
139
 * the old call contents and call answer_preprocess
140
 */
141
static inline int answer_will_preprocess(call_t *call)
142
{
143
    if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME)
144
        return 1;
1060 palkovsky 145
    if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTMETO)
146
        return 1;
1027 palkovsky 147
    return 0;
148
}
149
 
150
/** Interpret process answer as control information */
1040 palkovsky 151
static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
1027 palkovsky 152
{
153
    int phoneid;
154
 
1040 palkovsky 155
    if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTTOME) {
156
        phoneid = IPC_GET_ARG3(*olddata);
1027 palkovsky 157
        if (IPC_GET_RETVAL(answer->data)) {
158
            /* The connection was not accepted */
159
            phone_dealloc(phoneid);
1040 palkovsky 160
        } else {
161
            /* The connection was accepted */
162
            phone_connect(phoneid,&answer->sender->answerbox);
1027 palkovsky 163
        }
1060 palkovsky 164
    } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTMETO) {
165
        /* If the users accepted call, connect */
166
        if (!IPC_GET_RETVAL(answer->data)) {
167
            printf("Connecting Phone %P\n",IPC_GET_ARG3(*olddata));
168
            ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
169
                      &TASK->answerbox);
170
        }
1027 palkovsky 171
    }
172
}
173
 
174
/****************************************************/
175
/* Functions called to process received call/answer
176
 * before passing to uspace
177
 */
178
 
179
/** Do basic kernel processing of received call answer */
180
static int process_answer(answerbox_t *box,call_t *call)
181
{
182
    return 0;
183
}
184
 
185
/** Do basic kernel processing of received call request
186
 *
187
 * @return 0 - the call should be passed to userspace, 1 - ignore call
188
 */
189
static int process_request(answerbox_t *box,call_t *call)
190
{
191
    int phoneid;
192
 
193
    if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
1040 palkovsky 194
        phoneid = phone_alloc();
1027 palkovsky 195
        if (phoneid < 0) { /* Failed to allocate phone */
196
            IPC_SET_RETVAL(call->data, ELIMIT);
197
            ipc_answer(box,call);
198
            return -1;
199
        }
200
        IPC_SET_ARG3(call->data, phoneid);
201
    }
202
    return 0;
203
}
204
 
205
/** Send a call over IPC, wait for reply, return to user
206
 *
207
 * @return Call identification, returns -1 on fatal error,
208
           -2 on 'Too many async request, handle answers first
209
 */
210
__native sys_ipc_call_sync_fast(__native phoneid, __native method,
211
                __native arg1, __native *data)
212
{
213
    call_t call;
214
    phone_t *phone;
215
 
216
    phone = get_phone(phoneid);
217
    if (!phone)
1040 palkovsky 218
        return ENOENT;
1027 palkovsky 219
 
1040 palkovsky 220
    if (is_system_method(method))
221
        return EPERM;
222
 
1027 palkovsky 223
    ipc_call_init(&call);
224
    IPC_SET_METHOD(call.data, method);
225
    IPC_SET_ARG1(call.data, arg1);
226
 
227
    ipc_call_sync(phone, &call);
228
 
229
    copy_to_uspace(data, &call.data, sizeof(call.data));
230
 
231
    return 0;
232
}
233
 
234
/** Synchronous IPC call allowing to send whole message */
235
__native sys_ipc_call_sync(__native phoneid, __native *question,
236
               __native *reply)
237
{
238
    call_t call;
239
    phone_t *phone;
240
 
241
    phone = get_phone(phoneid);
242
    if (!phone)
1040 palkovsky 243
        return ENOENT;
1027 palkovsky 244
 
245
    ipc_call_init(&call);
246
    copy_from_uspace(&call.data, question, sizeof(call.data));
1040 palkovsky 247
 
248
    if (is_system_method(IPC_GET_METHOD(call.data)))
249
        return EPERM;
1027 palkovsky 250
 
251
    ipc_call_sync(phone, &call);
252
 
253
    copy_to_uspace(reply, &call.data, sizeof(call.data));
254
 
255
    return 0;
256
}
257
 
258
/** Check that the task did not exceed allowed limit
259
 *
260
 * @return 0 - Limit OK,   -1 - limit exceeded
261
 */
262
static int check_call_limit(void)
263
{
264
    if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
265
        atomic_dec(&TASK->active_calls);
266
        return -1;
267
    }
268
    return 0;
269
}
270
 
271
/** Send an asynchronous call over ipc
272
 *
273
 * @return Call identification, returns -1 on fatal error,
274
           -2 on 'Too many async request, handle answers first
275
 */
276
__native sys_ipc_call_async_fast(__native phoneid, __native method,
277
                 __native arg1, __native arg2)
278
{
279
    call_t *call;
280
    phone_t *phone;
281
 
282
    phone = get_phone(phoneid);
283
    if (!phone)
284
        return IPC_CALLRET_FATAL;
285
 
1040 palkovsky 286
    if (is_system_method(method))
287
        return IPC_CALLRET_FATAL;
288
 
1027 palkovsky 289
    if (check_call_limit())
290
        return IPC_CALLRET_TEMPORARY;
291
 
292
    call = ipc_call_alloc();
293
    IPC_SET_METHOD(call->data, method);
294
    IPC_SET_ARG1(call->data, arg1);
295
    IPC_SET_ARG2(call->data, arg2);
296
 
297
    ipc_call(phone, call);
298
 
299
    return (__native) call;
300
}
301
 
302
/** Synchronous IPC call allowing to send whole message
303
 *
304
 * @return The same as sys_ipc_call_async
305
 */
306
__native sys_ipc_call_async(__native phoneid, __native *data)
307
{
308
    call_t *call;
309
    phone_t *phone;
310
 
311
    phone = get_phone(phoneid);
312
    if (!phone)
313
        return IPC_CALLRET_FATAL;
314
 
315
    if (check_call_limit())
316
        return IPC_CALLRET_TEMPORARY;
317
 
318
    call = ipc_call_alloc();
319
    copy_from_uspace(&call->data, data, sizeof(call->data));
1040 palkovsky 320
 
321
    if (is_system_method(IPC_GET_METHOD(call->data))) {
322
        ipc_call_free(call);
323
        return EPERM;
324
    }
1027 palkovsky 325
 
326
    ipc_call(phone, call);
327
 
328
    return (__native) call;
329
}
330
 
1040 palkovsky 331
/** Forward received call to another destination
332
 *
333
 * The arg1 and arg2 are changed in the forwarded message
1060 palkovsky 334
 *
335
 * Warning: If implementing non-fast version, make sure that
336
 *          arg3 is not rewritten for certain system IPC
1040 palkovsky 337
 */
338
__native sys_ipc_forward_fast(__native callid, __native phoneid,
339
                  __native method, __native arg1)
340
{
341
    call_t *call;
342
    phone_t *phone;
343
 
344
    call = get_call(callid);
345
    if (!call)
346
        return ENOENT;
347
 
348
    phone = get_phone(phoneid);
349
    if (!phone) {
350
        IPC_SET_RETVAL(call->data, EFORWARD);
351
        ipc_answer(&TASK->answerbox, call);
352
        return ENOENT;
353
    }
354
 
355
    if (!is_forwardable(IPC_GET_METHOD(call->data))) {
356
        IPC_SET_RETVAL(call->data, EFORWARD);
357
        ipc_answer(&TASK->answerbox, call);
358
        return EPERM;
359
    }
360
 
361
    /* Userspace is not allowed to change method of system methods
362
     * on forward, allow changing ARG1 and ARG2 by means of method and arg1
363
     */
364
    if (is_system_method(IPC_GET_METHOD(call->data))) {
365
        IPC_SET_ARG1(call->data, method);
366
        IPC_SET_ARG2(call->data, arg1);
367
    } else {
368
        IPC_SET_METHOD(call->data, method);
369
        IPC_SET_ARG1(call->data, arg1);
370
    }
371
 
372
    ipc_forward(call, phone->callee, &TASK->answerbox);
373
 
374
    return 0;
375
}
376
 
1027 palkovsky 377
/** Send IPC answer */
378
__native sys_ipc_answer_fast(__native callid, __native retval,
379
                 __native arg1, __native arg2)
380
{
381
    call_t *call;
1040 palkovsky 382
    ipc_data_t saved_data;
1027 palkovsky 383
    int preprocess = 0;
384
 
1040 palkovsky 385
    call = get_call(callid);
386
    if (!call)
387
        return ENOENT;
1027 palkovsky 388
 
389
    if (answer_will_preprocess(call)) {
1040 palkovsky 390
        memcpy(&saved_data, &call->data, sizeof(call->data));
1027 palkovsky 391
        preprocess = 1;
392
    }
393
 
394
    IPC_SET_RETVAL(call->data, retval);
395
    IPC_SET_ARG1(call->data, arg1);
396
    IPC_SET_ARG2(call->data, arg2);
1040 palkovsky 397
 
1027 palkovsky 398
    if (preprocess)
1040 palkovsky 399
        answer_preprocess(call, &saved_data);
1027 palkovsky 400
 
401
    ipc_answer(&TASK->answerbox, call);
402
    return 0;
403
}
404
 
405
/** Send IPC answer */
406
inline __native sys_ipc_answer(__native callid, __native *data)
407
{
408
    call_t *call;
1040 palkovsky 409
    ipc_data_t saved_data;
1027 palkovsky 410
    int preprocess = 0;
411
 
1040 palkovsky 412
    call = get_call(callid);
413
    if (!call)
414
        return ENOENT;
1027 palkovsky 415
 
416
    if (answer_will_preprocess(call)) {
1040 palkovsky 417
        memcpy(&saved_data, &call->data, sizeof(call->data));
1027 palkovsky 418
        preprocess = 1;
419
    }
420
    copy_from_uspace(&call->data, data, sizeof(call->data));
421
 
422
    if (preprocess)
1040 palkovsky 423
        answer_preprocess(call, &saved_data);
1027 palkovsky 424
 
425
    ipc_answer(&TASK->answerbox, call);
426
 
427
    return 0;
428
}
429
 
430
/** Ask the other side of connection to do 'callback' connection
431
 *
432
 * @return 0 if no error, error otherwise
433
 */
434
__native sys_ipc_connect_to_me(__native phoneid, __native arg1,
435
                   __native arg2, task_id_t *taskid)
436
{
437
    call_t call;
438
    phone_t *phone;
439
 
440
    phone = get_phone(phoneid);
441
    if (!phone)
1040 palkovsky 442
        return ENOENT;
1027 palkovsky 443
 
444
    ipc_call_init(&call);
445
    IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME);
446
    IPC_SET_ARG1(call.data, arg1);
447
    IPC_SET_ARG2(call.data, arg2);
448
 
449
    ipc_call_sync(phone, &call);
450
 
451
    if (!IPC_GET_RETVAL(call.data) && taskid)
452
        copy_to_uspace(taskid,
453
                   &phone->callee->task->taskid,
454
                   sizeof(TASK->taskid));
1060 palkovsky 455
 
1027 palkovsky 456
    return IPC_GET_RETVAL(call.data);
457
}
458
 
1040 palkovsky 459
/** Ask target process to connect me somewhere
460
 *
461
 * @return phoneid - on success, error otherwise
462
 */
463
__native sys_ipc_connect_me_to(__native phoneid, __native arg1,
464
                   __native arg2)
465
{
466
    call_t call;
467
    phone_t *phone;
1060 palkovsky 468
    int newphid;
1040 palkovsky 469
 
470
    phone = get_phone(phoneid);
471
    if (!phone)
472
        return ENOENT;
473
 
1060 palkovsky 474
    newphid = phone_alloc();
475
    if (newphid < 0)
476
        return ELIMIT;
477
 
1040 palkovsky 478
    ipc_call_init(&call);
479
    IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO);
480
    IPC_SET_ARG1(call.data, arg1);
481
    IPC_SET_ARG2(call.data, arg2);
1060 palkovsky 482
    IPC_SET_ARG3(call.data, (__native)&TASK->phones[newphid]);
1040 palkovsky 483
 
484
    ipc_call_sync(phone, &call);
1060 palkovsky 485
 
486
    if (IPC_GET_RETVAL(call.data)) { /* Connection failed */
487
        phone_dealloc(newphid);
488
        return IPC_GET_RETVAL(call.data);
1040 palkovsky 489
    }
490
 
1060 palkovsky 491
    return newphid;
1040 palkovsky 492
}
493
 
1027 palkovsky 494
/** Wait for incoming ipc call or answer
495
 *
496
 * Generic function - can serve either as inkernel or userspace call
497
 * - inside kernel does probably unnecessary copying of data (TODO)
498
 *
499
 * @param result
500
 * @param taskid
501
 * @param flags
502
 * @return Callid, if callid & 1, then the call is answer
503
 */
504
inline __native sys_ipc_wait_for_call(ipc_data_t *calldata,
505
                      task_id_t *taskid,
506
                      __native flags)
507
 
508
{
509
    call_t *call;
510
 
511
restart:   
512
    call = ipc_wait_for_call(&TASK->answerbox, flags);
513
 
514
    if (call->flags & IPC_CALL_ANSWERED) {
515
        if (process_answer(&TASK->answerbox, call))
516
            goto restart;
517
 
518
        copy_to_uspace(calldata, &call->data, sizeof(call->data));
519
        atomic_dec(&TASK->active_calls);
520
        ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
521
        ipc_call_free(call);
522
 
523
        return ((__native)call) | IPC_CALLID_ANSWERED;
524
    }
525
    if (process_request(&TASK->answerbox, call))
526
        goto restart;
527
    copy_to_uspace(calldata, &call->data, sizeof(call->data));
528
    copy_to_uspace(taskid, (void *)&TASK->taskid, sizeof(TASK->taskid));
529
    return (__native)call;
530
}
531