source: mainline/generic/src/ipc/sysipc.c@ 8d6bc2d5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8d6bc2d5 was 8d6bc2d5, checked in by Jakub Jermar <jakub@…>, 20 years ago

Reimplement memory sharing. Currently, only
anonymous address space areas can be shared.

  • Property mode set to 100644
File size: 13.8 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#include <proc/thread.h>
32#include <errno.h>
33#include <memstr.h>
34#include <debug.h>
35#include <ipc/ipc.h>
36#include <ipc/sysipc.h>
37#include <ipc/irq.h>
38#include <ipc/ipcrsc.h>
39#include <arch/interrupt.h>
40#include <print.h>
41#include <syscall/copy.h>
42#include <security/cap.h>
43#include <mm/as.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 || method == IPC_M_AS_AREA_SEND)
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 for answer_preprocess
79 */
80static inline int answer_need_old(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 if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_SEND)
87 return 1;
88 return 0;
89}
90
91/** Interpret process answer as control information */
92static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
93{
94 int phoneid;
95
96 if (IPC_GET_RETVAL(answer->data) == EHANGUP) {
97 /* In case of forward, hangup the forwared phone,
98 * not the originator
99 */
100 spinlock_lock(&answer->data.phone->lock);
101 spinlock_lock(&TASK->answerbox.lock);
102 if (answer->data.phone->callee) {
103 list_remove(&answer->data.phone->list);
104 answer->data.phone->callee = 0;
105 }
106 spinlock_unlock(&TASK->answerbox.lock);
107 spinlock_unlock(&answer->data.phone->lock);
108 }
109
110 if (!olddata)
111 return 0;
112
113 if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
114 phoneid = IPC_GET_ARG3(*olddata);
115 if (IPC_GET_RETVAL(answer->data)) {
116 /* The connection was not accepted */
117 phone_dealloc(phoneid);
118 } else {
119 /* The connection was accepted */
120 phone_connect(phoneid,&answer->sender->answerbox);
121 /* Set 'phone identification' as arg3 of response */
122 IPC_SET_ARG3(answer->data, (__native)&TASK->phones[phoneid]);
123 }
124 } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
125 /* If the users accepted call, connect */
126 if (!IPC_GET_RETVAL(answer->data)) {
127 ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
128 &TASK->answerbox);
129 }
130 } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_SEND) {
131 if (!IPC_GET_RETVAL(answer->data)) { /* Accepted, handle as_area receipt */
132 ipl_t ipl;
133 as_t *as;
134
135 ipl = interrupts_disable();
136 spinlock_lock(&answer->sender->lock);
137 as = answer->sender->as;
138 spinlock_unlock(&answer->sender->lock);
139 interrupts_restore(ipl);
140
141 return as_area_share(as, IPC_GET_ARG2(*olddata),IPC_GET_ARG3(*olddata),
142 IPC_GET_ARG1(answer->data));
143 }
144 }
145 return 0;
146}
147
148/** Called before the request is sent
149 *
150 * @return 0 - no error, -1 - report error to user
151 */
152static int request_preprocess(call_t *call)
153{
154 int newphid;
155 size_t size;
156
157 switch (IPC_GET_METHOD(call->data)) {
158 case IPC_M_CONNECT_ME_TO:
159 newphid = phone_alloc();
160 if (newphid < 0)
161 return ELIMIT;
162 /* Set arg3 for server */
163 IPC_SET_ARG3(call->data, (__native)&TASK->phones[newphid]);
164 call->flags |= IPC_CALL_CONN_ME_TO;
165 call->private = newphid;
166 break;
167 case IPC_M_AS_AREA_SEND:
168 size = as_get_size(IPC_GET_ARG2(call->data));
169 if (!size) {
170 return EPERM;
171 }
172 IPC_SET_ARG3(call->data, size);
173 break;
174 default:
175 break;
176 }
177 return 0;
178}
179
180/****************************************************/
181/* Functions called to process received call/answer
182 * before passing to uspace
183 */
184
185/** Do basic kernel processing of received call answer */
186static void process_answer(call_t *call)
187{
188 if (IPC_GET_RETVAL(call->data) == EHANGUP && \
189 call->flags & IPC_CALL_FORWARDED)
190 IPC_SET_RETVAL(call->data, EFORWARD);
191
192 if (call->flags & IPC_CALL_CONN_ME_TO) {
193 if (IPC_GET_RETVAL(call->data))
194 phone_dealloc(call->private);
195 else
196 IPC_SET_ARG3(call->data, call->private);
197 }
198}
199
200/** Do basic kernel processing of received call request
201 *
202 * @return 0 - the call should be passed to userspace, 1 - ignore call
203 */
204static int process_request(answerbox_t *box,call_t *call)
205{
206 int phoneid;
207
208 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
209 phoneid = phone_alloc();
210 if (phoneid < 0) { /* Failed to allocate phone */
211 IPC_SET_RETVAL(call->data, ELIMIT);
212 ipc_answer(box,call);
213 return -1;
214 }
215 IPC_SET_ARG3(call->data, phoneid);
216 }
217 return 0;
218}
219
220/** Send a call over IPC, wait for reply, return to user
221 *
222 * @return Call identification, returns -1 on fatal error,
223 -2 on 'Too many async request, handle answers first
224 */
225__native sys_ipc_call_sync_fast(__native phoneid, __native method,
226 __native arg1, ipc_data_t *data)
227{
228 call_t call;
229 phone_t *phone;
230 int res;
231
232 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
233
234 ipc_call_static_init(&call);
235 IPC_SET_METHOD(call.data, method);
236 IPC_SET_ARG1(call.data, arg1);
237
238 if (!(res=request_preprocess(&call))) {
239 ipc_call_sync(phone, &call);
240 process_answer(&call);
241 } else
242 IPC_SET_RETVAL(call.data, res);
243 STRUCT_TO_USPACE(&data->args, &call.data.args);
244
245 return 0;
246}
247
248/** Synchronous IPC call allowing to send whole message */
249__native sys_ipc_call_sync(__native phoneid, ipc_data_t *question,
250 ipc_data_t *reply)
251{
252 call_t call;
253 phone_t *phone;
254 int res;
255 int rc;
256
257 ipc_call_static_init(&call);
258 rc = copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
259 if (rc != 0)
260 return (__native) rc;
261
262 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
263
264 if (!(res=request_preprocess(&call))) {
265 ipc_call_sync(phone, &call);
266 process_answer(&call);
267 } else
268 IPC_SET_RETVAL(call.data, res);
269
270 rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
271 if (rc != 0)
272 return rc;
273
274 return 0;
275}
276
277/** Check that the task did not exceed allowed limit
278 *
279 * @return 0 - Limit OK, -1 - limit exceeded
280 */
281static int check_call_limit(void)
282{
283 if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
284 atomic_dec(&TASK->active_calls);
285 return -1;
286 }
287 return 0;
288}
289
290/** Send an asynchronous call over ipc
291 *
292 * @return Call identification, returns -1 on fatal error,
293 -2 on 'Too many async request, handle answers first
294 */
295__native sys_ipc_call_async_fast(__native phoneid, __native method,
296 __native arg1, __native arg2)
297{
298 call_t *call;
299 phone_t *phone;
300 int res;
301
302 if (check_call_limit())
303 return IPC_CALLRET_TEMPORARY;
304
305 GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
306
307 call = ipc_call_alloc(0);
308 IPC_SET_METHOD(call->data, method);
309 IPC_SET_ARG1(call->data, arg1);
310 IPC_SET_ARG2(call->data, arg2);
311
312 if (!(res=request_preprocess(call)))
313 ipc_call(phone, call);
314 else
315 ipc_backsend_err(phone, call, res);
316
317 return (__native) call;
318}
319
320/** Synchronous IPC call allowing to send whole message
321 *
322 * @return The same as sys_ipc_call_async
323 */
324__native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
325{
326 call_t *call;
327 phone_t *phone;
328 int res;
329 int rc;
330
331 if (check_call_limit())
332 return IPC_CALLRET_TEMPORARY;
333
334 GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
335
336 call = ipc_call_alloc(0);
337 rc = copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
338 if (rc != 0) {
339 ipc_call_free(call);
340 return (__native) rc;
341 }
342 if (!(res=request_preprocess(call)))
343 ipc_call(phone, call);
344 else
345 ipc_backsend_err(phone, call, res);
346
347 return (__native) call;
348}
349
350/** Forward received call to another destination
351 *
352 * The arg1 and arg2 are changed in the forwarded message
353 *
354 * Warning: If implementing non-fast version, make sure that
355 * arg3 is not rewritten for certain system IPC
356 */
357__native sys_ipc_forward_fast(__native callid, __native phoneid,
358 __native method, __native arg1)
359{
360 call_t *call;
361 phone_t *phone;
362
363 call = get_call(callid);
364 if (!call)
365 return ENOENT;
366
367 call->flags |= IPC_CALL_FORWARDED;
368
369 GET_CHECK_PHONE(phone, phoneid, {
370 IPC_SET_RETVAL(call->data, EFORWARD);
371 ipc_answer(&TASK->answerbox, call);
372 return ENOENT;
373 });
374
375 if (!is_forwardable(IPC_GET_METHOD(call->data))) {
376 IPC_SET_RETVAL(call->data, EFORWARD);
377 ipc_answer(&TASK->answerbox, call);
378 return EPERM;
379 }
380
381 /* Userspace is not allowed to change method of system methods
382 * on forward, allow changing ARG1 and ARG2 by means of method and arg1
383 */
384 if (is_system_method(IPC_GET_METHOD(call->data))) {
385 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
386 phone_dealloc(IPC_GET_ARG3(call->data));
387
388 IPC_SET_ARG1(call->data, method);
389 IPC_SET_ARG2(call->data, arg1);
390 } else {
391 IPC_SET_METHOD(call->data, method);
392 IPC_SET_ARG1(call->data, arg1);
393 }
394
395 return ipc_forward(call, phone, &TASK->answerbox);
396}
397
398/** Send IPC answer */
399__native sys_ipc_answer_fast(__native callid, __native retval,
400 __native arg1, __native arg2)
401{
402 call_t *call;
403 ipc_data_t saved_data;
404 int saveddata = 0;
405 int rc;
406
407 /* Do not answer notification callids */
408 if (callid & IPC_CALLID_NOTIFICATION)
409 return 0;
410
411 call = get_call(callid);
412 if (!call)
413 return ENOENT;
414
415 if (answer_need_old(call)) {
416 memcpy(&saved_data, &call->data, sizeof(call->data));
417 saveddata = 1;
418 }
419
420 IPC_SET_RETVAL(call->data, retval);
421 IPC_SET_ARG1(call->data, arg1);
422 IPC_SET_ARG2(call->data, arg2);
423 rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
424
425 ipc_answer(&TASK->answerbox, call);
426 return rc;
427}
428
429/** Send IPC answer */
430__native sys_ipc_answer(__native callid, ipc_data_t *data)
431{
432 call_t *call;
433 ipc_data_t saved_data;
434 int saveddata = 0;
435 int rc;
436
437 /* Do not answer notification callids */
438 if (callid & IPC_CALLID_NOTIFICATION)
439 return 0;
440
441 call = get_call(callid);
442 if (!call)
443 return ENOENT;
444
445 if (answer_need_old(call)) {
446 memcpy(&saved_data, &call->data, sizeof(call->data));
447 saveddata = 1;
448 }
449 rc = copy_from_uspace(&call->data.args, &data->args,
450 sizeof(call->data.args));
451 if (rc != 0)
452 return rc;
453
454 rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
455
456 ipc_answer(&TASK->answerbox, call);
457
458 return rc;
459}
460
461/** Hang up the phone
462 *
463 */
464__native sys_ipc_hangup(int phoneid)
465{
466 phone_t *phone;
467
468 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
469
470 if (ipc_phone_hangup(phone))
471 return -1;
472
473 return 0;
474}
475
476/** Wait for incoming ipc call or answer
477 *
478 * @param calldata Pointer to buffer where the call/answer data is stored
479 * @param usec Timeout. See waitq_sleep_timeout() for explanation.
480 * @param nonblocking See waitq_sleep_timeout() for explanation.
481 *
482 * @return Callid, if callid & 1, then the call is answer
483 */
484__native sys_ipc_wait_for_call(ipc_data_t *calldata, __u32 usec, int nonblocking)
485{
486 call_t *call;
487
488restart:
489 call = ipc_wait_for_call(&TASK->answerbox, usec, nonblocking);
490 if (!call)
491 return 0;
492
493 if (call->flags & IPC_CALL_NOTIF) {
494 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
495 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
496 ipc_call_free(call);
497
498 return ((__native)call) | IPC_CALLID_NOTIFICATION;
499 }
500
501 if (call->flags & IPC_CALL_ANSWERED) {
502 process_answer(call);
503
504 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
505
506 atomic_dec(&TASK->active_calls);
507
508 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
509 ipc_call_free(call);
510 goto restart;
511 }
512
513 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
514 ipc_call_free(call);
515
516 return ((__native)call) | IPC_CALLID_ANSWERED;
517 }
518
519 if (process_request(&TASK->answerbox, call))
520 goto restart;
521
522 /* Include phone address('id') of the caller in the request,
523 * copy whole call->data, not only call->data.args */
524 if (STRUCT_TO_USPACE(calldata, &call->data)) {
525 return 0;
526 }
527 return (__native)call;
528}
529
530/** Connect irq handler to task */
531__native sys_ipc_register_irq(__native irq, irq_code_t *ucode)
532{
533 if (!(cap_get(TASK) & CAP_IRQ_REG))
534 return EPERM;
535
536 if (irq >= IRQ_COUNT)
537 return (__native) ELIMIT;
538
539 irq_ipc_bind_arch(irq);
540
541 return ipc_irq_register(&TASK->answerbox, irq, ucode);
542}
543
544/* Disconnect irq handler from task */
545__native sys_ipc_unregister_irq(__native irq)
546{
547 if (!(cap_get(TASK) & CAP_IRQ_REG))
548 return EPERM;
549
550 if (irq >= IRQ_COUNT)
551 return (__native) ELIMIT;
552
553 ipc_irq_unregister(&TASK->answerbox, irq);
554
555 return 0;
556}
Note: See TracBrowser for help on using the repository browser.