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

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

Untested better IPC functions.

  • There is some bug in MIPS, unpredicatbly sometimes the thread gets mapped

different frame for stack.

  • Property mode set to 100644
File size: 11.6 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 <ipc/ipcrsc.h>
39
40
41#include <print.h>
42#include <arch.h>
43#include <proc/thread.h>
44
45#define GET_CHECK_PHONE(phone,phoneid,err) { \
46 if (phoneid > IPC_MAX_PHONES) { err; } \
47 phone = &TASK->phones[phoneid]; \
48}
49
50#define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*src))
51
52/** Return true if the method is a system method */
53static inline int is_system_method(__native method)
54{
55 if (method <= IPC_M_LAST_SYSTEM)
56 return 1;
57 return 0;
58}
59
60/** Return true if the message with this method is forwardable
61 *
62 * - some system messages may be forwarded, for some of them
63 * it is useless
64 */
65static inline int is_forwardable(__native method)
66{
67 if (method == IPC_M_PHONE_HUNGUP)
68 return 0; /* This message is meant only for the receiver */
69 return 1;
70}
71
72/****************************************************/
73/* Functions that preprocess answer before sending
74 * it to the recepient
75 */
76
77/** Return true if the caller (ipc_answer) should save
78 * the old call contents and call answer_preprocess
79 */
80static inline int answer_will_preprocess(call_t *call)
81{
82 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
83 return 1;
84 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
85 return 1;
86 return 0;
87}
88
89/** Interpret process answer as control information */
90static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
91{
92 int phoneid;
93
94 if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
95 phoneid = IPC_GET_ARG3(*olddata);
96 if (IPC_GET_RETVAL(answer->data)) {
97 /* The connection was not accepted */
98 phone_dealloc(phoneid);
99 } else {
100 /* The connection was accepted */
101 phone_connect(phoneid,&answer->sender->answerbox);
102 }
103 } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
104 /* If the users accepted call, connect */
105 if (!IPC_GET_RETVAL(answer->data)) {
106 ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
107 &TASK->answerbox);
108 }
109 }
110}
111
112/****************************************************/
113/* Functions called to process received call/answer
114 * before passing to uspace
115 */
116
117/** Do basic kernel processing of received call answer */
118static int process_answer(answerbox_t *box,call_t *call)
119{
120 return 0;
121}
122
123/** Do basic kernel processing of received call request
124 *
125 * @return 0 - the call should be passed to userspace, 1 - ignore call
126 */
127static int process_request(answerbox_t *box,call_t *call)
128{
129 int phoneid;
130
131 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
132 phoneid = phone_alloc();
133 if (phoneid < 0) { /* Failed to allocate phone */
134 IPC_SET_RETVAL(call->data, ELIMIT);
135 ipc_answer(box,call);
136 return -1;
137 }
138 IPC_SET_ARG3(call->data, phoneid);
139 }
140 return 0;
141}
142
143/** Send a call over IPC, wait for reply, return to user
144 *
145 * @return Call identification, returns -1 on fatal error,
146 -2 on 'Too many async request, handle answers first
147 */
148__native sys_ipc_call_sync_fast(__native phoneid, __native method,
149 __native arg1, ipc_data_t *data)
150{
151 call_t call;
152 phone_t *phone;
153
154 if (is_system_method(method))
155 return EPERM;
156
157 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
158
159 ipc_call_static_init(&call);
160 IPC_SET_METHOD(call.data, method);
161 IPC_SET_ARG1(call.data, arg1);
162
163 ipc_call_sync(phone, &call);
164
165 STRUCT_TO_USPACE(&data->args, &call.data.args);
166
167 return 0;
168}
169
170/** Synchronous IPC call allowing to send whole message */
171__native sys_ipc_call_sync(__native phoneid, ipc_data_t *question,
172 ipc_data_t *reply)
173{
174 call_t call;
175 phone_t *phone;
176
177 ipc_call_static_init(&call);
178 copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
179
180 if (is_system_method(IPC_GET_METHOD(call.data)))
181 return EPERM;
182
183 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
184
185 ipc_call_sync(phone, &call);
186
187 STRUCT_TO_USPACE(&reply->args, &call.data.args);
188
189 return 0;
190}
191
192/** Check that the task did not exceed allowed limit
193 *
194 * @return 0 - Limit OK, -1 - limit exceeded
195 */
196static int check_call_limit(void)
197{
198 if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
199 atomic_dec(&TASK->active_calls);
200 return -1;
201 }
202 return 0;
203}
204
205/** Send an asynchronous call over ipc
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_async_fast(__native phoneid, __native method,
211 __native arg1, __native arg2)
212{
213 call_t *call;
214 phone_t *phone;
215
216 if (is_system_method(method))
217 return IPC_CALLRET_FATAL;
218
219 if (check_call_limit())
220 return IPC_CALLRET_TEMPORARY;
221
222 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
223
224 call = ipc_call_alloc();
225 IPC_SET_METHOD(call->data, method);
226 IPC_SET_ARG1(call->data, arg1);
227 IPC_SET_ARG2(call->data, arg2);
228
229 ipc_call(phone, call);
230
231 return (__native) call;
232}
233
234/** Synchronous IPC call allowing to send whole message
235 *
236 * @return The same as sys_ipc_call_async
237 */
238__native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
239{
240 call_t *call;
241 phone_t *phone;
242
243 if (check_call_limit())
244 return IPC_CALLRET_TEMPORARY;
245
246 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
247
248 call = ipc_call_alloc();
249 copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
250
251 if (is_system_method(IPC_GET_METHOD(call->data))) {
252 ipc_call_free(call);
253 return EPERM;
254 }
255
256 ipc_call(phone, call);
257
258 return (__native) call;
259}
260
261/** Forward received call to another destination
262 *
263 * The arg1 and arg2 are changed in the forwarded message
264 *
265 * Warning: If implementing non-fast version, make sure that
266 * arg3 is not rewritten for certain system IPC
267 */
268__native sys_ipc_forward_fast(__native callid, __native phoneid,
269 __native method, __native arg1)
270{
271 call_t *call;
272 phone_t *phone;
273
274 call = get_call(callid);
275 if (!call)
276 return ENOENT;
277
278 GET_CHECK_PHONE(phone, phoneid, {
279 IPC_SET_RETVAL(call->data, EFORWARD);
280 ipc_answer(&TASK->answerbox, call);
281 return ENOENT;
282 });
283
284 if (!is_forwardable(IPC_GET_METHOD(call->data))) {
285 IPC_SET_RETVAL(call->data, EFORWARD);
286 ipc_answer(&TASK->answerbox, call);
287 return EPERM;
288 }
289
290 /* Userspace is not allowed to change method of system methods
291 * on forward, allow changing ARG1 and ARG2 by means of method and arg1
292 */
293 if (is_system_method(IPC_GET_METHOD(call->data))) {
294 IPC_SET_ARG1(call->data, method);
295 IPC_SET_ARG2(call->data, arg1);
296 } else {
297 IPC_SET_METHOD(call->data, method);
298 IPC_SET_ARG1(call->data, arg1);
299 }
300
301 ipc_forward(call, phone, &TASK->answerbox);
302
303 return 0;
304}
305
306/** Send IPC answer */
307__native sys_ipc_answer_fast(__native callid, __native retval,
308 __native arg1, __native arg2)
309{
310 call_t *call;
311 ipc_data_t saved_data;
312 int preprocess = 0;
313
314 call = get_call(callid);
315 if (!call)
316 return ENOENT;
317
318 if (answer_will_preprocess(call)) {
319 memcpy(&saved_data, &call->data, sizeof(call->data));
320 preprocess = 1;
321 }
322
323 IPC_SET_RETVAL(call->data, retval);
324 IPC_SET_ARG1(call->data, arg1);
325 IPC_SET_ARG2(call->data, arg2);
326
327 if (preprocess)
328 answer_preprocess(call, &saved_data);
329
330 ipc_answer(&TASK->answerbox, call);
331 return 0;
332}
333
334/** Send IPC answer */
335__native sys_ipc_answer(__native callid, ipc_data_t *data)
336{
337 call_t *call;
338 ipc_data_t saved_data;
339 int preprocess = 0;
340
341 call = get_call(callid);
342 if (!call)
343 return ENOENT;
344
345 if (answer_will_preprocess(call)) {
346 memcpy(&saved_data, &call->data, sizeof(call->data));
347 preprocess = 1;
348 }
349 copy_from_uspace(&call->data.args, &data->args,
350 sizeof(call->data.args));
351
352 if (preprocess)
353 answer_preprocess(call, &saved_data);
354
355 ipc_answer(&TASK->answerbox, call);
356
357 return 0;
358}
359
360/** Ask the other side of connection to do 'callback' connection
361 *
362 * @return 0 if no error, error otherwise
363 */
364__native sys_ipc_connect_to_me(__native phoneid, __native arg1,
365 __native arg2, task_id_t *taskid)
366{
367 call_t call;
368 phone_t *phone;
369
370 ipc_call_static_init(&call);
371 IPC_SET_METHOD(call.data, IPC_M_CONNECT_TO_ME);
372 IPC_SET_ARG1(call.data, arg1);
373 IPC_SET_ARG2(call.data, arg2);
374
375 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
376
377 ipc_call_sync(phone, &call);
378
379 if (!IPC_GET_RETVAL(call.data) && taskid)
380 STRUCT_TO_USPACE(taskid, &phone->callee->task->taskid);
381
382 return IPC_GET_RETVAL(call.data);
383}
384
385/** Ask target process to connect me somewhere
386 *
387 * @return phoneid - on success, error otherwise
388 */
389__native sys_ipc_connect_me_to(__native phoneid, __native arg1,
390 __native arg2)
391{
392 call_t call;
393 phone_t *phone;
394 int newphid;
395
396 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
397
398 newphid = phone_alloc();
399 if (newphid < 0)
400 return ELIMIT;
401
402 ipc_call_static_init(&call);
403 IPC_SET_METHOD(call.data, IPC_M_CONNECT_ME_TO);
404 IPC_SET_ARG1(call.data, arg1);
405 IPC_SET_ARG2(call.data, arg2);
406 IPC_SET_ARG3(call.data, (__native)&TASK->phones[newphid]);
407
408 ipc_call_sync(phone, &call);
409
410 if (IPC_GET_RETVAL(call.data)) { /* Connection failed */
411 phone_dealloc(newphid);
412 return IPC_GET_RETVAL(call.data);
413 }
414
415 return newphid;
416}
417
418/** Hang up the phone
419 *
420 *
421 *
422 */
423__native sys_ipc_hangup(int phoneid)
424{
425 phone_t *phone;
426
427 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
428
429 if (ipc_phone_hangup(phone))
430 return -1;
431
432 return 0;
433}
434
435/** Wait for incoming ipc call or answer
436 *
437 * @param calldata Pointer to buffer where the call/answer data is stored
438 * @param taskid On 'CONNECT_ME_TO' call it is filled with 'taskid' of
439 * the caller.
440 * @param flags
441 * @return Callid, if callid & 1, then the call is answer
442 */
443__native sys_ipc_wait_for_call(ipc_data_t *calldata, task_id_t *taskid,
444 __native flags)
445
446{
447 call_t *call;
448
449restart:
450 call = ipc_wait_for_call(&TASK->answerbox, flags);
451
452 if (call->flags & IPC_CALL_ANSWERED) {
453 if (process_answer(&TASK->answerbox, call))
454 goto restart;
455
456 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
457
458 atomic_dec(&TASK->active_calls);
459
460 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
461 ipc_call_free(call);
462 goto restart;
463 }
464
465 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
466 ipc_call_free(call);
467
468 return ((__native)call) | IPC_CALLID_ANSWERED;
469 }
470
471 if (process_request(&TASK->answerbox, call))
472 goto restart;
473
474 /* Include phone address('id') of the caller in the request */
475 STRUCT_TO_USPACE(calldata, &call->data);
476 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
477 STRUCT_TO_USPACE(taskid, &TASK->taskid);
478 return (__native)call;
479}
Note: See TracBrowser for help on using the repository browser.