source: mainline/generic/src/ipc/sysipc.c@ dabe6333

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since dabe6333 was 2ba7810, checked in by Ondrej Palkovsky <ondrap@…>, 20 years ago

Changed ipc to use spinlocks instead of mutexes again.
Fixed loading inits to set nameservice as the first loaded process.
Lot of TODO in ipc done.

  • Property mode set to 100644
File size: 11.9 KB
Line 
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
45/** Return true if the method is a system method */
46static 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 */
58static 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 */
67static 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
74/** Return pointer to phone identified by phoneid or NULL if non-existent */
75static 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 */
89static int phone_alloc(void)
90{
91 int i;
92
93 spinlock_lock(&TASK->lock);
94
95 for (i=0; i < IPC_MAX_PHONES; i++) {
96 if (!TASK->phones[i].busy) {
97 TASK->phones[i].busy = 1;
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 */
109static void phone_dealloc(int phoneid)
110{
111 spinlock_lock(&TASK->lock);
112
113 ASSERT(TASK->phones[phoneid].busy);
114
115 if (TASK->phones[phoneid].callee)
116 ipc_phone_destroy(&TASK->phones[phoneid]);
117
118 TASK->phones[phoneid].busy = 0;
119 spinlock_unlock(&TASK->lock);
120}
121
122static 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
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 */
137static 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 */
145static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
146{
147 int phoneid;
148
149 if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTTOME) {
150 phoneid = IPC_GET_ARG3(*olddata);
151 if (IPC_GET_RETVAL(answer->data)) {
152 /* The connection was not accepted */
153 phone_dealloc(phoneid);
154 } else {
155 /* The connection was accepted */
156 phone_connect(phoneid,&answer->sender->answerbox);
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 */
167static 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 */
176static int process_request(answerbox_t *box,call_t *call)
177{
178 int phoneid;
179
180 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
181 phoneid = phone_alloc();
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)
205 return ENOENT;
206
207 if (is_system_method(method))
208 return EPERM;
209
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)
230 return ENOENT;
231
232 ipc_call_init(&call);
233 copy_from_uspace(&call.data, question, sizeof(call.data));
234
235 if (is_system_method(IPC_GET_METHOD(call.data)))
236 return EPERM;
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 */
249static 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
273 if (is_system_method(method))
274 return IPC_CALLRET_FATAL;
275
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));
307
308 if (is_system_method(IPC_GET_METHOD(call->data))) {
309 ipc_call_free(call);
310 return EPERM;
311 }
312
313 ipc_call(phone, call);
314
315 return (__native) call;
316}
317
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
361/** Send IPC answer */
362__native sys_ipc_answer_fast(__native callid, __native retval,
363 __native arg1, __native arg2)
364{
365 call_t *call;
366 ipc_data_t saved_data;
367 int preprocess = 0;
368
369 call = get_call(callid);
370 if (!call)
371 return ENOENT;
372
373 if (answer_will_preprocess(call)) {
374 memcpy(&saved_data, &call->data, sizeof(call->data));
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);
381
382 if (preprocess)
383 answer_preprocess(call, &saved_data);
384
385 ipc_answer(&TASK->answerbox, call);
386 return 0;
387}
388
389/** Send IPC answer */
390inline __native sys_ipc_answer(__native callid, __native *data)
391{
392 call_t *call;
393 ipc_data_t saved_data;
394 int preprocess = 0;
395
396 call = get_call(callid);
397 if (!call)
398 return ENOENT;
399
400 if (answer_will_preprocess(call)) {
401 memcpy(&saved_data, &call->data, sizeof(call->data));
402 preprocess = 1;
403 }
404 copy_from_uspace(&call->data, data, sizeof(call->data));
405
406 if (preprocess)
407 answer_preprocess(call, &saved_data);
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)
426 return ENOENT;
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
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
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 */
479inline __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
486restart:
487 call = ipc_wait_for_call(&TASK->answerbox, flags);
488 printf("Received call %P from sender: %P\n", call, call->sender);
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
Note: See TracBrowser for help on using the repository browser.