Subversion Repositories HelenOS-historic

Rev

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