source: mainline/kernel/generic/src/ipc/sysipc.c@ 7975433

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

Cleanup of forgotten calls on answer.

  • Property mode set to 100644
File size: 34.3 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/** @addtogroup genericipc
30 * @{
31 */
32/** @file
33 */
34
35#include <arch.h>
36#include <proc/task.h>
37#include <proc/thread.h>
38#include <errno.h>
39#include <memstr.h>
40#include <debug.h>
41#include <ipc/ipc.h>
42#include <abi/ipc/methods.h>
43#include <ipc/sysipc.h>
44#include <ipc/irq.h>
45#include <ipc/ipcrsc.h>
46#include <ipc/event.h>
47#include <ipc/kbox.h>
48#include <synch/waitq.h>
49#include <udebug/udebug_ipc.h>
50#include <arch/interrupt.h>
51#include <syscall/copy.h>
52#include <security/cap.h>
53#include <console/console.h>
54#include <mm/as.h>
55#include <print.h>
56#include <macros.h>
57
58/**
59 * Maximum buffer size allowed for IPC_M_DATA_WRITE and IPC_M_DATA_READ
60 * requests.
61 */
62#define DATA_XFER_LIMIT (64 * 1024)
63
64#define STRUCT_TO_USPACE(dst, src) copy_to_uspace((dst), (src), sizeof(*(src)))
65
66/** Get phone from the current task by ID.
67 *
68 * @param phoneid Phone ID.
69 * @param phone Place to store pointer to phone.
70 *
71 * @return EOK on success, EINVAL if ID is invalid.
72 *
73 */
74static int phone_get(sysarg_t phoneid, phone_t **phone)
75{
76 if (phoneid >= IPC_MAX_PHONES)
77 return EINVAL;
78
79 *phone = &TASK->phones[phoneid];
80 return EOK;
81}
82
83/** Decide if the interface and method is a system method.
84 *
85 * @param imethod Interface and method to be decided.
86 *
87 * @return True if the interface and method is a system
88 * interface and method.
89 *
90 */
91static inline bool method_is_system(sysarg_t imethod)
92{
93 if (imethod <= IPC_M_LAST_SYSTEM)
94 return true;
95
96 return false;
97}
98
99/** Decide if the message with this interface and method is forwardable.
100 *
101 * Some system messages may be forwarded, for some of them
102 * it is useless.
103 *
104 * @param imethod Interface and method to be decided.
105 *
106 * @return True if the interface and method is forwardable.
107 *
108 */
109static inline bool method_is_forwardable(sysarg_t imethod)
110{
111 switch (imethod) {
112 case IPC_M_CONNECTION_CLONE:
113 case IPC_M_CLONE_ESTABLISH:
114 case IPC_M_PHONE_HUNGUP:
115 /* This message is meant only for the original recipient. */
116 return false;
117 default:
118 return true;
119 }
120}
121
122/** Decide if the message with this interface and method is immutable on forward.
123 *
124 * Some system messages may be forwarded but their content cannot be altered.
125 *
126 * @param imethod Interface and method to be decided.
127 *
128 * @return True if the interface and method is immutable on forward.
129 *
130 */
131static inline bool method_is_immutable(sysarg_t imethod)
132{
133 switch (imethod) {
134 case IPC_M_SHARE_OUT:
135 case IPC_M_SHARE_IN:
136 case IPC_M_DATA_WRITE:
137 case IPC_M_DATA_READ:
138 case IPC_M_STATE_CHANGE_AUTHORIZE:
139 return true;
140 default:
141 return false;
142 }
143}
144
145
146/***********************************************************************
147 * Functions that preprocess answer before sending it to the recepient.
148 ***********************************************************************/
149
150/** Decide if the caller (e.g. ipc_answer()) should save the old call contents
151 * for answer_preprocess().
152 *
153 * @param call Call structure to be decided.
154 *
155 * @return true if the old call contents should be saved.
156 *
157 */
158static inline bool answer_need_old(call_t *call)
159{
160 switch (IPC_GET_IMETHOD(call->data)) {
161 case IPC_M_CONNECTION_CLONE:
162 case IPC_M_CLONE_ESTABLISH:
163 case IPC_M_CONNECT_TO_ME:
164 case IPC_M_CONNECT_ME_TO:
165 case IPC_M_SHARE_OUT:
166 case IPC_M_SHARE_IN:
167 case IPC_M_DATA_WRITE:
168 case IPC_M_DATA_READ:
169 case IPC_M_STATE_CHANGE_AUTHORIZE:
170 return true;
171 default:
172 return false;
173 }
174}
175
176static void cleanup_m_connection_clone(call_t *answer, ipc_data_t *olddata)
177{
178 int phoneid = (int) IPC_GET_ARG1(*olddata);
179 phone_t *phone = &TASK->phones[phoneid];
180
181 /*
182 * In this case, the connection was established at the request time and
183 * therefore we need to slam the phone. We don't merely hangup as that
184 * would result in sending IPC_M_HUNGUP to the third party on the other
185 * side of the cloned phone.
186 */
187 mutex_lock(&phone->lock);
188 if (phone->state == IPC_PHONE_CONNECTED) {
189 irq_spinlock_lock(&phone->callee->lock, true);
190 list_remove(&phone->link);
191 phone->state = IPC_PHONE_SLAMMED;
192 irq_spinlock_unlock(&phone->callee->lock, true);
193 }
194 mutex_unlock(&phone->lock);
195}
196
197static int a_preprocess_m_connection_clone(call_t *answer, ipc_data_t *olddata)
198{
199 if (IPC_GET_RETVAL(answer->data) != EOK) {
200 /*
201 * The recipient of the cloned phone rejected the offer.
202 */
203 cleanup_m_connection_clone(answer, olddata);
204 }
205
206 return EOK;
207}
208
209static int a_preprocess_m_clone_establish(call_t *answer, ipc_data_t *olddata)
210{
211 phone_t *phone = (phone_t *) IPC_GET_ARG5(*olddata);
212
213 if (IPC_GET_RETVAL(answer->data) != EOK) {
214 /*
215 * The other party on the cloned phone rejected our request
216 * for connection on the protocol level. We need to break the
217 * connection without sending IPC_M_HUNGUP back.
218 */
219 mutex_lock(&phone->lock);
220 if (phone->state == IPC_PHONE_CONNECTED) {
221 irq_spinlock_lock(&phone->callee->lock, true);
222 list_remove(&phone->link);
223 phone->state = IPC_PHONE_SLAMMED;
224 irq_spinlock_unlock(&phone->callee->lock, true);
225 }
226 mutex_unlock(&phone->lock);
227 }
228
229 return EOK;
230}
231
232static void cleanup_m_connect_to_me(call_t *answer, ipc_data_t *olddata)
233{
234 int phoneid = (int) IPC_GET_ARG5(*olddata);
235
236 phone_dealloc(phoneid);
237}
238
239static int a_preprocess_m_connect_to_me(call_t *answer, ipc_data_t *olddata)
240{
241 int phoneid = (int) IPC_GET_ARG5(*olddata);
242
243 if (IPC_GET_RETVAL(answer->data) != EOK) {
244 /* The connection was not accepted */
245 cleanup_m_connect_to_me(answer, olddata);
246 } else {
247 /* The connection was accepted */
248 phone_connect(phoneid, &answer->sender->answerbox);
249 /* Set 'phone hash' as arg5 of response */
250 IPC_SET_ARG5(answer->data, (sysarg_t) &TASK->phones[phoneid]);
251 }
252
253 return EOK;
254}
255
256static void cleanup_m_connect_me_to(call_t *answer, ipc_data_t *olddata)
257{
258 phone_dealloc(answer->priv);
259}
260
261static int a_preprocess_m_connect_me_to(call_t *answer, ipc_data_t *olddata)
262{
263 phone_t *phone = (phone_t *) IPC_GET_ARG5(*olddata);
264
265 /* If the user accepted call, connect */
266 if (IPC_GET_RETVAL(answer->data) == EOK)
267 ipc_phone_connect(phone, &TASK->answerbox);
268
269 return EOK;
270}
271
272static int a_preprocess_m_share_out(call_t *answer, ipc_data_t *olddata)
273{
274 int rc = EOK;
275
276 if (!IPC_GET_RETVAL(answer->data)) {
277 /* Accepted, handle as_area receipt */
278
279 irq_spinlock_lock(&answer->sender->lock, true);
280 as_t *as = answer->sender->as;
281 irq_spinlock_unlock(&answer->sender->lock, true);
282
283 uintptr_t dst_base = (uintptr_t) -1;
284 rc = as_area_share(as, IPC_GET_ARG1(*olddata),
285 IPC_GET_ARG2(*olddata), AS, IPC_GET_ARG3(*olddata),
286 &dst_base, IPC_GET_ARG1(answer->data));
287
288 if (rc == EOK) {
289 rc = copy_to_uspace((void *) IPC_GET_ARG2(answer->data),
290 &dst_base, sizeof(dst_base));
291 }
292
293 IPC_SET_RETVAL(answer->data, rc);
294 }
295
296 return rc;
297}
298
299static int a_preprocess_m_share_in(call_t *answer, ipc_data_t *olddata)
300{
301 if (!IPC_GET_RETVAL(answer->data)) {
302 irq_spinlock_lock(&answer->sender->lock, true);
303 as_t *as = answer->sender->as;
304 irq_spinlock_unlock(&answer->sender->lock, true);
305
306 uintptr_t dst_base = (uintptr_t) -1;
307 int rc = as_area_share(AS, IPC_GET_ARG1(answer->data),
308 IPC_GET_ARG1(*olddata), as, IPC_GET_ARG2(answer->data),
309 &dst_base, IPC_GET_ARG3(answer->data));
310 IPC_SET_ARG4(answer->data, dst_base);
311 IPC_SET_RETVAL(answer->data, rc);
312 }
313
314 return EOK;
315}
316
317static int a_preprocess_m_data_read(call_t *answer, ipc_data_t *olddata)
318{
319 ASSERT(!answer->buffer);
320 if (!IPC_GET_RETVAL(answer->data)) {
321 /* The recipient agreed to send data. */
322 uintptr_t src = IPC_GET_ARG1(answer->data);
323 uintptr_t dst = IPC_GET_ARG1(*olddata);
324 size_t max_size = IPC_GET_ARG2(*olddata);
325 size_t size = IPC_GET_ARG2(answer->data);
326 if (size && size <= max_size) {
327 /*
328 * Copy the destination VA so that this piece of
329 * information is not lost.
330 */
331 IPC_SET_ARG1(answer->data, dst);
332
333 answer->buffer = malloc(size, 0);
334 int rc = copy_from_uspace(answer->buffer,
335 (void *) src, size);
336 if (rc) {
337 IPC_SET_RETVAL(answer->data, rc);
338 free(answer->buffer);
339 answer->buffer = NULL;
340 }
341 } else if (!size) {
342 IPC_SET_RETVAL(answer->data, EOK);
343 } else {
344 IPC_SET_RETVAL(answer->data, ELIMIT);
345 }
346 }
347
348 return EOK;
349}
350
351static int a_preprocess_m_data_write(call_t *answer, ipc_data_t *olddata)
352{
353 ASSERT(answer->buffer);
354 if (!IPC_GET_RETVAL(answer->data)) {
355 /* The recipient agreed to receive data. */
356 uintptr_t dst = (uintptr_t)IPC_GET_ARG1(answer->data);
357 size_t size = (size_t)IPC_GET_ARG2(answer->data);
358 size_t max_size = (size_t)IPC_GET_ARG2(*olddata);
359
360 if (size <= max_size) {
361 int rc = copy_to_uspace((void *) dst,
362 answer->buffer, size);
363 if (rc)
364 IPC_SET_RETVAL(answer->data, rc);
365 } else {
366 IPC_SET_RETVAL(answer->data, ELIMIT);
367 }
368 }
369 free(answer->buffer);
370 answer->buffer = NULL;
371
372 return EOK;
373}
374
375static int
376a_preprocess_m_state_change_authorize(call_t *answer, ipc_data_t *olddata)
377{
378 int rc = EOK;
379
380 if (!IPC_GET_RETVAL(answer->data)) {
381 /* The recipient authorized the change of state. */
382 phone_t *recipient_phone;
383 task_t *other_task_s;
384 task_t *other_task_r;
385
386 rc = phone_get(IPC_GET_ARG1(answer->data),
387 &recipient_phone);
388 if (rc != EOK) {
389 IPC_SET_RETVAL(answer->data, ENOENT);
390 return ENOENT;
391 }
392
393 mutex_lock(&recipient_phone->lock);
394 if (recipient_phone->state != IPC_PHONE_CONNECTED) {
395 mutex_unlock(&recipient_phone->lock);
396 IPC_SET_RETVAL(answer->data, EINVAL);
397 return EINVAL;
398 }
399
400 other_task_r = recipient_phone->callee->task;
401 other_task_s = (task_t *) IPC_GET_ARG5(*olddata);
402
403 /*
404 * See if both the sender and the recipient meant the
405 * same third party task.
406 */
407 if (other_task_r != other_task_s) {
408 IPC_SET_RETVAL(answer->data, EINVAL);
409 rc = EINVAL;
410 } else {
411 rc = event_task_notify_5(other_task_r,
412 EVENT_TASK_STATE_CHANGE, false,
413 IPC_GET_ARG1(*olddata),
414 IPC_GET_ARG2(*olddata),
415 IPC_GET_ARG3(*olddata),
416 LOWER32(olddata->task_id),
417 UPPER32(olddata->task_id));
418 IPC_SET_RETVAL(answer->data, rc);
419 }
420
421 mutex_unlock(&recipient_phone->lock);
422 }
423
424 return rc;
425}
426
427/** Cleanup additional resources associated with the answer. */
428static void cleanup_forgotten(call_t *answer, ipc_data_t *olddata)
429{
430 if (!olddata)
431 return;
432
433 switch (IPC_GET_IMETHOD(*olddata)) {
434 case IPC_M_CONNECTION_CLONE:
435 cleanup_m_connection_clone(answer, olddata);
436 break;
437 case IPC_M_CONNECT_TO_ME:
438 cleanup_m_connect_to_me(answer, olddata);
439 break;
440 case IPC_M_CONNECT_ME_TO:
441 cleanup_m_connect_me_to(answer, olddata);
442 break;
443 default:
444 break;
445 }
446}
447
448/** Interpret process answer as control information.
449 *
450 * This function is called directly after sys_ipc_answer().
451 *
452 * @param answer Call structure with the answer.
453 * @param olddata Saved data of the request.
454 *
455 * @return Return EOK on success or a negative error code.
456 *
457 */
458static int answer_preprocess(call_t *answer, ipc_data_t *olddata)
459{
460 int rc = EOK;
461
462 spinlock_lock(&answer->forget_lock);
463 if (answer->forget) {
464 /*
465 * This is a forgotten call and answer->sender is not valid.
466 */
467 spinlock_unlock(&answer->forget_lock);
468 cleanup_forgotten(answer, olddata);
469 return rc;
470 } else {
471 /*
472 * Hold the sender task so that it cannot suddenly disappear
473 * while we are working with it.
474 */
475 task_hold(answer->sender);
476 }
477 spinlock_unlock(&answer->forget_lock);
478
479 if ((native_t) IPC_GET_RETVAL(answer->data) == EHANGUP) {
480 /* In case of forward, hangup the forwared phone,
481 * not the originator
482 */
483 mutex_lock(&answer->data.phone->lock);
484 irq_spinlock_lock(&TASK->answerbox.lock, true);
485 if (answer->data.phone->state == IPC_PHONE_CONNECTED) {
486 list_remove(&answer->data.phone->link);
487 answer->data.phone->state = IPC_PHONE_SLAMMED;
488 }
489 irq_spinlock_unlock(&TASK->answerbox.lock, true);
490 mutex_unlock(&answer->data.phone->lock);
491 }
492
493 if (!olddata) {
494 task_release(answer->sender);
495 return rc;
496 }
497
498 switch (IPC_GET_IMETHOD(*olddata)) {
499 case IPC_M_CONNECTION_CLONE:
500 rc = a_preprocess_m_connection_clone(answer, olddata);
501 break;
502 case IPC_M_CLONE_ESTABLISH:
503 rc = a_preprocess_m_clone_establish(answer, olddata);
504 break;
505 case IPC_M_CONNECT_TO_ME:
506 rc = a_preprocess_m_connect_to_me(answer, olddata);
507 break;
508 case IPC_M_CONNECT_ME_TO:
509 rc = a_preprocess_m_connect_me_to(answer, olddata);
510 break;
511 case IPC_M_SHARE_OUT:
512 rc = a_preprocess_m_share_out(answer, olddata);
513 break;
514 case IPC_M_SHARE_IN:
515 rc = a_preprocess_m_share_in(answer, olddata);
516 break;
517 case IPC_M_DATA_READ:
518 rc = a_preprocess_m_data_read(answer, olddata);
519 break;
520 case IPC_M_DATA_WRITE:
521 rc = a_preprocess_m_data_write(answer, olddata);
522 break;
523 case IPC_M_STATE_CHANGE_AUTHORIZE:
524 rc = a_preprocess_m_state_change_authorize(answer, olddata);
525 break;
526 default:
527 break;
528 }
529
530 task_release(answer->sender);
531
532 return rc;
533}
534
535static void phones_lock(phone_t *p1, phone_t *p2)
536{
537 if (p1 < p2) {
538 mutex_lock(&p1->lock);
539 mutex_lock(&p2->lock);
540 } else if (p1 > p2) {
541 mutex_lock(&p2->lock);
542 mutex_lock(&p1->lock);
543 } else
544 mutex_lock(&p1->lock);
545}
546
547static void phones_unlock(phone_t *p1, phone_t *p2)
548{
549 mutex_unlock(&p1->lock);
550 if (p1 != p2)
551 mutex_unlock(&p2->lock);
552}
553
554static int r_preprocess_m_connection_clone(call_t *call, phone_t *phone)
555{
556 phone_t *cloned_phone;
557
558 if (phone_get(IPC_GET_ARG1(call->data), &cloned_phone) != EOK)
559 return ENOENT;
560
561 phones_lock(cloned_phone, phone);
562
563 if ((cloned_phone->state != IPC_PHONE_CONNECTED) ||
564 phone->state != IPC_PHONE_CONNECTED) {
565 phones_unlock(cloned_phone, phone);
566 return EINVAL;
567 }
568
569 /*
570 * We can be pretty sure now that both tasks exist and we are
571 * connected to them. As we continue to hold the phone locks,
572 * we are effectively preventing them from finishing their
573 * potential cleanup.
574 *
575 */
576 int newphid = phone_alloc(phone->callee->task);
577 if (newphid < 0) {
578 phones_unlock(cloned_phone, phone);
579 return ELIMIT;
580 }
581
582 ipc_phone_connect(&phone->callee->task->phones[newphid],
583 cloned_phone->callee);
584 phones_unlock(cloned_phone, phone);
585
586 /* Set the new phone for the callee. */
587 IPC_SET_ARG1(call->data, newphid);
588
589 return EOK;
590}
591
592static int r_preprocess_m_clone_establish(call_t *call, phone_t *phone)
593{
594 IPC_SET_ARG5(call->data, (sysarg_t) phone);
595
596 return EOK;
597}
598
599static int r_preprocess_m_connect_me_to(call_t *call, phone_t *phone)
600{
601 int newphid = phone_alloc(TASK);
602
603 if (newphid < 0)
604 return ELIMIT;
605
606 /* Set arg5 for server */
607 IPC_SET_ARG5(call->data, (sysarg_t) &TASK->phones[newphid]);
608 call->flags |= IPC_CALL_CONN_ME_TO;
609 call->priv = newphid;
610
611 return EOK;
612}
613
614static int r_preprocess_m_share_out(call_t *call, phone_t *phone)
615{
616 size_t size = as_area_get_size(IPC_GET_ARG1(call->data));
617
618 if (!size)
619 return EPERM;
620 IPC_SET_ARG2(call->data, size);
621
622 return EOK;
623}
624
625static int r_preprocess_m_data_read(call_t *call, phone_t *phone)
626{
627 size_t size = IPC_GET_ARG2(call->data);
628
629 if (size > DATA_XFER_LIMIT) {
630 int flags = IPC_GET_ARG3(call->data);
631
632 if (flags & IPC_XF_RESTRICT)
633 IPC_SET_ARG2(call->data, DATA_XFER_LIMIT);
634 else
635 return ELIMIT;
636 }
637
638 return EOK;
639}
640
641static int r_preprocess_m_data_write(call_t *call, phone_t *phone)
642{
643 uintptr_t src = IPC_GET_ARG1(call->data);
644 size_t size = IPC_GET_ARG2(call->data);
645
646 if (size > DATA_XFER_LIMIT) {
647 int flags = IPC_GET_ARG3(call->data);
648
649 if (flags & IPC_XF_RESTRICT) {
650 size = DATA_XFER_LIMIT;
651 IPC_SET_ARG2(call->data, size);
652 } else
653 return ELIMIT;
654 }
655
656 call->buffer = (uint8_t *) malloc(size, 0);
657 int rc = copy_from_uspace(call->buffer, (void *) src, size);
658 if (rc != 0) {
659 free(call->buffer);
660 return rc;
661 }
662
663 return EOK;
664}
665
666static int r_preprocess_m_state_change_authorize(call_t *call, phone_t *phone)
667{
668 phone_t *sender_phone;
669 task_t *other_task_s;
670
671 if (phone_get(IPC_GET_ARG5(call->data), &sender_phone) != EOK)
672 return ENOENT;
673
674 mutex_lock(&sender_phone->lock);
675 if (sender_phone->state != IPC_PHONE_CONNECTED) {
676 mutex_unlock(&sender_phone->lock);
677 return EINVAL;
678 }
679
680 other_task_s = sender_phone->callee->task;
681
682 mutex_unlock(&sender_phone->lock);
683
684 /* Remember the third party task hash. */
685 IPC_SET_ARG5(call->data, (sysarg_t) other_task_s);
686
687 return EOK;
688}
689
690/** Called before the request is sent.
691 *
692 * @param call Call structure with the request.
693 * @param phone Phone that the call will be sent through.
694 *
695 * @return Return 0 on success, ELIMIT or EPERM on error.
696 *
697 */
698static int request_preprocess(call_t *call, phone_t *phone)
699{
700 int rc = EOK;
701
702 switch (IPC_GET_IMETHOD(call->data)) {
703 case IPC_M_CONNECTION_CLONE:
704 rc = r_preprocess_m_connection_clone(call, phone);
705 break;
706 case IPC_M_CLONE_ESTABLISH:
707 rc = r_preprocess_m_clone_establish(call, phone);
708 break;
709 case IPC_M_CONNECT_ME_TO:
710 rc = r_preprocess_m_connect_me_to(call, phone);
711 break;
712 case IPC_M_SHARE_OUT:
713 rc = r_preprocess_m_share_out(call, phone);
714 break;
715 case IPC_M_DATA_READ:
716 rc = r_preprocess_m_data_read(call, phone);
717 break;
718 case IPC_M_DATA_WRITE:
719 rc = r_preprocess_m_data_write(call, phone);
720 break;
721 case IPC_M_STATE_CHANGE_AUTHORIZE:
722 rc = r_preprocess_m_state_change_authorize(call, phone);
723 break;
724#ifdef CONFIG_UDEBUG
725 case IPC_M_DEBUG:
726 rc = udebug_request_preprocess(call, phone);
727 break;
728#endif
729 default:
730 break;
731 }
732
733 return rc;
734}
735
736/*******************************************************************************
737 * Functions called to process received call/answer before passing it to uspace.
738 *******************************************************************************/
739
740/** Do basic kernel processing of received call answer.
741 *
742 * @param call Call structure with the answer.
743 *
744 */
745static void process_answer(call_t *call)
746{
747 if (((native_t) IPC_GET_RETVAL(call->data) == EHANGUP) &&
748 (call->flags & IPC_CALL_FORWARDED))
749 IPC_SET_RETVAL(call->data, EFORWARD);
750
751 if (call->flags & IPC_CALL_CONN_ME_TO) {
752 if (IPC_GET_RETVAL(call->data))
753 phone_dealloc(call->priv);
754 else
755 IPC_SET_ARG5(call->data, call->priv);
756 }
757
758 if (call->buffer) {
759 /*
760 * This must be an affirmative answer to IPC_M_DATA_READ
761 * or IPC_M_DEBUG/UDEBUG_M_MEM_READ...
762 *
763 */
764 uintptr_t dst = IPC_GET_ARG1(call->data);
765 size_t size = IPC_GET_ARG2(call->data);
766 int rc = copy_to_uspace((void *) dst, call->buffer, size);
767 if (rc)
768 IPC_SET_RETVAL(call->data, rc);
769 free(call->buffer);
770 call->buffer = NULL;
771 }
772}
773
774static int r_process_m_connect_to_me(answerbox_t *box, call_t *call)
775{
776 int phoneid = phone_alloc(TASK);
777
778 if (phoneid < 0) { /* Failed to allocate phone */
779 IPC_SET_RETVAL(call->data, ELIMIT);
780 ipc_answer(box, call);
781 return -1;
782 }
783
784 IPC_SET_ARG5(call->data, phoneid);
785
786 return EOK;
787}
788
789static int r_process_m_debug(answerbox_t *box, call_t *call)
790{
791 return -1;
792}
793
794/** Do basic kernel processing of received call request.
795 *
796 * @param box Destination answerbox structure.
797 * @param call Call structure with the request.
798 *
799 * @return 0 if the call should be passed to userspace.
800 * @return -1 if the call should be ignored.
801 *
802 */
803static int process_request(answerbox_t *box, call_t *call)
804{
805 int rc = EOK;
806
807 switch (IPC_GET_IMETHOD(call->data)) {
808 case IPC_M_CONNECT_TO_ME:
809 rc = r_process_m_connect_to_me(box, call);
810 break;
811 case IPC_M_DEBUG:
812 rc = r_process_m_debug(box, call);
813 break;
814 default:
815 break;
816 }
817
818 return rc;
819}
820
821/** Check that the task did not exceed the allowed limit of asynchronous calls
822 * made over a phone.
823 *
824 * @param phone Phone to check the limit against.
825 *
826 * @return 0 if limit not reached or -1 if limit exceeded.
827 *
828 */
829static int check_call_limit(phone_t *phone)
830{
831 if (atomic_get(&phone->active_calls) >= IPC_MAX_ASYNC_CALLS)
832 return -1;
833
834 return 0;
835}
836
837/** Make a fast asynchronous call over IPC.
838 *
839 * This function can only handle four arguments of payload, but is faster than
840 * the generic function sys_ipc_call_async_slow().
841 *
842 * @param phoneid Phone handle for the call.
843 * @param imethod Interface and method of the call.
844 * @param arg1 Service-defined payload argument.
845 * @param arg2 Service-defined payload argument.
846 * @param arg3 Service-defined payload argument.
847 * @param arg4 Service-defined payload argument.
848 *
849 * @return Call hash on success.
850 * @return IPC_CALLRET_FATAL in case of a fatal error.
851 * @return IPC_CALLRET_TEMPORARY if there are too many pending
852 * asynchronous requests; answers should be handled first.
853 *
854 */
855sysarg_t sys_ipc_call_async_fast(sysarg_t phoneid, sysarg_t imethod,
856 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
857{
858 phone_t *phone;
859 if (phone_get(phoneid, &phone) != EOK)
860 return IPC_CALLRET_FATAL;
861
862 if (check_call_limit(phone))
863 return IPC_CALLRET_TEMPORARY;
864
865 call_t *call = ipc_call_alloc(0);
866 IPC_SET_IMETHOD(call->data, imethod);
867 IPC_SET_ARG1(call->data, arg1);
868 IPC_SET_ARG2(call->data, arg2);
869 IPC_SET_ARG3(call->data, arg3);
870 IPC_SET_ARG4(call->data, arg4);
871
872 /*
873 * To achieve deterministic behavior, zero out arguments that are beyond
874 * the limits of the fast version.
875 */
876 IPC_SET_ARG5(call->data, 0);
877
878 int res = request_preprocess(call, phone);
879
880 if (!res)
881 ipc_call(phone, call);
882 else
883 ipc_backsend_err(phone, call, res);
884
885 return (sysarg_t) call;
886}
887
888/** Make an asynchronous IPC call allowing to transmit the entire payload.
889 *
890 * @param phoneid Phone handle for the call.
891 * @param data Userspace address of call data with the request.
892 *
893 * @return See sys_ipc_call_async_fast().
894 *
895 */
896sysarg_t sys_ipc_call_async_slow(sysarg_t phoneid, ipc_data_t *data)
897{
898 phone_t *phone;
899 if (phone_get(phoneid, &phone) != EOK)
900 return IPC_CALLRET_FATAL;
901
902 if (check_call_limit(phone))
903 return IPC_CALLRET_TEMPORARY;
904
905 call_t *call = ipc_call_alloc(0);
906 int rc = copy_from_uspace(&call->data.args, &data->args,
907 sizeof(call->data.args));
908 if (rc != 0) {
909 ipc_call_free(call);
910 return (sysarg_t) rc;
911 }
912
913 int res = request_preprocess(call, phone);
914
915 if (!res)
916 ipc_call(phone, call);
917 else
918 ipc_backsend_err(phone, call, res);
919
920 return (sysarg_t) call;
921}
922
923/** Forward a received call to another destination
924 *
925 * Common code for both the fast and the slow version.
926 *
927 * @param callid Hash of the call to forward.
928 * @param phoneid Phone handle to use for forwarding.
929 * @param imethod New interface and method to use for the forwarded call.
930 * @param arg1 New value of the first argument for the forwarded call.
931 * @param arg2 New value of the second argument for the forwarded call.
932 * @param arg3 New value of the third argument for the forwarded call.
933 * @param arg4 New value of the fourth argument for the forwarded call.
934 * @param arg5 New value of the fifth argument for the forwarded call.
935 * @param mode Flags that specify mode of the forward operation.
936 * @param slow If true, arg3, arg4 and arg5 are considered. Otherwise
937 * the function considers only the fast version arguments:
938 * i.e. arg1 and arg2.
939 *
940 * @return 0 on succes, otherwise an error code.
941 *
942 * Warning: Make sure that ARG5 is not rewritten for certain system IPC
943 *
944 */
945static sysarg_t sys_ipc_forward_common(sysarg_t callid, sysarg_t phoneid,
946 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
947 sysarg_t arg4, sysarg_t arg5, unsigned int mode, bool slow)
948{
949 call_t *call = get_call(callid);
950 if (!call)
951 return ENOENT;
952
953 call->flags |= IPC_CALL_FORWARDED;
954
955 phone_t *phone;
956 if (phone_get(phoneid, &phone) != EOK) {
957 IPC_SET_RETVAL(call->data, EFORWARD);
958 ipc_answer(&TASK->answerbox, call);
959 return ENOENT;
960 }
961
962 if (!method_is_forwardable(IPC_GET_IMETHOD(call->data))) {
963 IPC_SET_RETVAL(call->data, EFORWARD);
964 ipc_answer(&TASK->answerbox, call);
965 return EPERM;
966 }
967
968 /*
969 * User space is not allowed to change interface and method of system
970 * methods on forward, allow changing ARG1, ARG2, ARG3 and ARG4 by
971 * means of imethod, arg1, arg2 and arg3.
972 * If the interface and method is immutable, don't change anything.
973 */
974 if (!method_is_immutable(IPC_GET_IMETHOD(call->data))) {
975 if (method_is_system(IPC_GET_IMETHOD(call->data))) {
976 if (IPC_GET_IMETHOD(call->data) == IPC_M_CONNECT_TO_ME)
977 phone_dealloc(IPC_GET_ARG5(call->data));
978
979 IPC_SET_ARG1(call->data, imethod);
980 IPC_SET_ARG2(call->data, arg1);
981 IPC_SET_ARG3(call->data, arg2);
982
983 if (slow)
984 IPC_SET_ARG4(call->data, arg3);
985
986 /*
987 * For system methods we deliberately don't
988 * overwrite ARG5.
989 */
990 } else {
991 IPC_SET_IMETHOD(call->data, imethod);
992 IPC_SET_ARG1(call->data, arg1);
993 IPC_SET_ARG2(call->data, arg2);
994 if (slow) {
995 IPC_SET_ARG3(call->data, arg3);
996 IPC_SET_ARG4(call->data, arg4);
997 IPC_SET_ARG5(call->data, arg5);
998 }
999 }
1000 }
1001
1002 return ipc_forward(call, phone, &TASK->answerbox, mode);
1003}
1004
1005/** Forward a received call to another destination - fast version.
1006 *
1007 * In case the original interface and method is a system method, ARG1, ARG2
1008 * and ARG3 are overwritten in the forwarded message with the new method and
1009 * the new arg1 and arg2, respectively. Otherwise the IMETHOD, ARG1 and ARG2
1010 * are rewritten with the new interface and method, arg1 and arg2, respectively.
1011 * Also note there is a set of immutable methods, for which the new method and
1012 * arguments are not set and these values are ignored.
1013 *
1014 * @param callid Hash of the call to forward.
1015 * @param phoneid Phone handle to use for forwarding.
1016 * @param imethod New interface and method to use for the forwarded call.
1017 * @param arg1 New value of the first argument for the forwarded call.
1018 * @param arg2 New value of the second argument for the forwarded call.
1019 * @param mode Flags that specify mode of the forward operation.
1020 *
1021 * @return 0 on succes, otherwise an error code.
1022 *
1023 */
1024sysarg_t sys_ipc_forward_fast(sysarg_t callid, sysarg_t phoneid,
1025 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
1026{
1027 return sys_ipc_forward_common(callid, phoneid, imethod, arg1, arg2, 0, 0,
1028 0, mode, false);
1029}
1030
1031/** Forward a received call to another destination - slow version.
1032 *
1033 * This function is the slow verision of the sys_ipc_forward_fast interface.
1034 * It can copy all five new arguments and the new interface and method from
1035 * the userspace. It naturally extends the functionality of the fast version.
1036 * For system methods, it additionally stores the new value of arg3 to ARG4.
1037 * For non-system methods, it additionally stores the new value of arg3, arg4
1038 * and arg5, respectively, to ARG3, ARG4 and ARG5, respectively.
1039 *
1040 * @param callid Hash of the call to forward.
1041 * @param phoneid Phone handle to use for forwarding.
1042 * @param data Userspace address of the new IPC data.
1043 * @param mode Flags that specify mode of the forward operation.
1044 *
1045 * @return 0 on succes, otherwise an error code.
1046 *
1047 */
1048sysarg_t sys_ipc_forward_slow(sysarg_t callid, sysarg_t phoneid,
1049 ipc_data_t *data, unsigned int mode)
1050{
1051 ipc_data_t newdata;
1052 int rc = copy_from_uspace(&newdata.args, &data->args,
1053 sizeof(newdata.args));
1054 if (rc != 0)
1055 return (sysarg_t) rc;
1056
1057 return sys_ipc_forward_common(callid, phoneid,
1058 IPC_GET_IMETHOD(newdata), IPC_GET_ARG1(newdata),
1059 IPC_GET_ARG2(newdata), IPC_GET_ARG3(newdata),
1060 IPC_GET_ARG4(newdata), IPC_GET_ARG5(newdata), mode, true);
1061}
1062
1063/** Answer an IPC call - fast version.
1064 *
1065 * This function can handle only two return arguments of payload, but is faster
1066 * than the generic sys_ipc_answer().
1067 *
1068 * @param callid Hash of the call to be answered.
1069 * @param retval Return value of the answer.
1070 * @param arg1 Service-defined return value.
1071 * @param arg2 Service-defined return value.
1072 * @param arg3 Service-defined return value.
1073 * @param arg4 Service-defined return value.
1074 *
1075 * @return 0 on success, otherwise an error code.
1076 *
1077 */
1078sysarg_t sys_ipc_answer_fast(sysarg_t callid, sysarg_t retval,
1079 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
1080{
1081 /* Do not answer notification callids */
1082 if (callid & IPC_CALLID_NOTIFICATION)
1083 return 0;
1084
1085 call_t *call = get_call(callid);
1086 if (!call)
1087 return ENOENT;
1088
1089 ipc_data_t saved_data;
1090 bool saved;
1091
1092 if (answer_need_old(call)) {
1093 memcpy(&saved_data, &call->data, sizeof(call->data));
1094 saved = true;
1095 } else
1096 saved = false;
1097
1098 IPC_SET_RETVAL(call->data, retval);
1099 IPC_SET_ARG1(call->data, arg1);
1100 IPC_SET_ARG2(call->data, arg2);
1101 IPC_SET_ARG3(call->data, arg3);
1102 IPC_SET_ARG4(call->data, arg4);
1103
1104 /*
1105 * To achieve deterministic behavior, zero out arguments that are beyond
1106 * the limits of the fast version.
1107 */
1108 IPC_SET_ARG5(call->data, 0);
1109 int rc = answer_preprocess(call, saved ? &saved_data : NULL);
1110
1111 ipc_answer(&TASK->answerbox, call);
1112 return rc;
1113}
1114
1115/** Answer an IPC call.
1116 *
1117 * @param callid Hash of the call to be answered.
1118 * @param data Userspace address of call data with the answer.
1119 *
1120 * @return 0 on success, otherwise an error code.
1121 *
1122 */
1123sysarg_t sys_ipc_answer_slow(sysarg_t callid, ipc_data_t *data)
1124{
1125 /* Do not answer notification callids */
1126 if (callid & IPC_CALLID_NOTIFICATION)
1127 return 0;
1128
1129 call_t *call = get_call(callid);
1130 if (!call)
1131 return ENOENT;
1132
1133 ipc_data_t saved_data;
1134 bool saved;
1135
1136 if (answer_need_old(call)) {
1137 memcpy(&saved_data, &call->data, sizeof(call->data));
1138 saved = true;
1139 } else
1140 saved = false;
1141
1142 int rc = copy_from_uspace(&call->data.args, &data->args,
1143 sizeof(call->data.args));
1144 if (rc != 0)
1145 return rc;
1146
1147 rc = answer_preprocess(call, saved ? &saved_data : NULL);
1148
1149 ipc_answer(&TASK->answerbox, call);
1150 return rc;
1151}
1152
1153/** Hang up a phone.
1154 *
1155 * @param Phone handle of the phone to be hung up.
1156 *
1157 * @return 0 on success or an error code.
1158 *
1159 */
1160sysarg_t sys_ipc_hangup(sysarg_t phoneid)
1161{
1162 phone_t *phone;
1163
1164 if (phone_get(phoneid, &phone) != EOK)
1165 return ENOENT;
1166
1167 if (ipc_phone_hangup(phone))
1168 return -1;
1169
1170 return 0;
1171}
1172
1173/** Wait for an incoming IPC call or an answer.
1174 *
1175 * @param calldata Pointer to buffer where the call/answer data is stored.
1176 * @param usec Timeout. See waitq_sleep_timeout() for explanation.
1177 * @param flags Select mode of sleep operation. See waitq_sleep_timeout()
1178 * for explanation.
1179 *
1180 * @return Hash of the call.
1181 * If IPC_CALLID_NOTIFICATION bit is set in the hash, the
1182 * call is a notification. IPC_CALLID_ANSWERED denotes an
1183 * answer.
1184 *
1185 */
1186sysarg_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec,
1187 unsigned int flags)
1188{
1189 call_t *call;
1190
1191restart:
1192
1193#ifdef CONFIG_UDEBUG
1194 udebug_stoppable_begin();
1195#endif
1196
1197 call = ipc_wait_for_call(&TASK->answerbox, usec,
1198 flags | SYNCH_FLAGS_INTERRUPTIBLE);
1199
1200#ifdef CONFIG_UDEBUG
1201 udebug_stoppable_end();
1202#endif
1203
1204 if (!call)
1205 return 0;
1206
1207 if (call->flags & IPC_CALL_NOTIF) {
1208 /* Set in_phone_hash to the interrupt counter */
1209 call->data.phone = (void *) call->priv;
1210
1211 STRUCT_TO_USPACE(calldata, &call->data);
1212
1213 ipc_call_free(call);
1214
1215 return ((sysarg_t) call) | IPC_CALLID_NOTIFICATION;
1216 }
1217
1218 if (call->flags & IPC_CALL_ANSWERED) {
1219 process_answer(call);
1220
1221 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
1222 ipc_call_free(call);
1223 goto restart;
1224 }
1225
1226 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
1227 ipc_call_free(call);
1228
1229 return ((sysarg_t) call) | IPC_CALLID_ANSWERED;
1230 }
1231
1232 if (process_request(&TASK->answerbox, call))
1233 goto restart;
1234
1235 /* Include phone address('id') of the caller in the request,
1236 * copy whole call->data, not only call->data.args */
1237 if (STRUCT_TO_USPACE(calldata, &call->data)) {
1238 /*
1239 * The callee will not receive this call and no one else has
1240 * a chance to answer it. Reply with the EPARTY error code.
1241 */
1242 ipc_data_t saved_data;
1243 bool saved;
1244
1245 if (answer_need_old(call)) {
1246 memcpy(&saved_data, &call->data, sizeof(call->data));
1247 saved = true;
1248 } else
1249 saved = false;
1250
1251 IPC_SET_RETVAL(call->data, EPARTY);
1252 (void) answer_preprocess(call, saved ? &saved_data : NULL);
1253 ipc_answer(&TASK->answerbox, call);
1254 return 0;
1255 }
1256
1257 return (sysarg_t) call;
1258}
1259
1260/** Interrupt one thread from sys_ipc_wait_for_call().
1261 *
1262 */
1263sysarg_t sys_ipc_poke(void)
1264{
1265 waitq_unsleep(&TASK->answerbox.wq);
1266 return EOK;
1267}
1268
1269/** Connect an IRQ handler to a task.
1270 *
1271 * @param inr IRQ number.
1272 * @param devno Device number.
1273 * @param imethod Interface and method to be associated with the notification.
1274 * @param ucode Uspace pointer to the top-half pseudocode.
1275 *
1276 * @return EPERM or a return code returned by ipc_irq_register().
1277 *
1278 */
1279sysarg_t sys_irq_register(inr_t inr, devno_t devno, sysarg_t imethod,
1280 irq_code_t *ucode)
1281{
1282 if (!(cap_get(TASK) & CAP_IRQ_REG))
1283 return EPERM;
1284
1285 return ipc_irq_register(&TASK->answerbox, inr, devno, imethod, ucode);
1286}
1287
1288/** Disconnect an IRQ handler from a task.
1289 *
1290 * @param inr IRQ number.
1291 * @param devno Device number.
1292 *
1293 * @return Zero on success or EPERM on error.
1294 *
1295 */
1296sysarg_t sys_irq_unregister(inr_t inr, devno_t devno)
1297{
1298 if (!(cap_get(TASK) & CAP_IRQ_REG))
1299 return EPERM;
1300
1301 ipc_irq_unregister(&TASK->answerbox, inr, devno);
1302
1303 return 0;
1304}
1305
1306#ifdef __32_BITS__
1307
1308/** Syscall connect to a task by ID (32 bits)
1309 *
1310 * @return Phone id on success, or negative error code.
1311 *
1312 */
1313sysarg_t sys_ipc_connect_kbox(sysarg64_t *uspace_taskid)
1314{
1315#ifdef CONFIG_UDEBUG
1316 sysarg64_t taskid;
1317 int rc = copy_from_uspace(&taskid, uspace_taskid, sizeof(sysarg64_t));
1318 if (rc != 0)
1319 return (sysarg_t) rc;
1320
1321 return ipc_connect_kbox((task_id_t) taskid);
1322#else
1323 return (sysarg_t) ENOTSUP;
1324#endif
1325}
1326
1327#endif /* __32_BITS__ */
1328
1329#ifdef __64_BITS__
1330
1331/** Syscall connect to a task by ID (64 bits)
1332 *
1333 * @return Phone id on success, or negative error code.
1334 *
1335 */
1336sysarg_t sys_ipc_connect_kbox(sysarg_t taskid)
1337{
1338#ifdef CONFIG_UDEBUG
1339 return ipc_connect_kbox((task_id_t) taskid);
1340#else
1341 return (sysarg_t) ENOTSUP;
1342#endif
1343}
1344
1345#endif /* __64_BITS__ */
1346
1347/** @}
1348 */
Note: See TracBrowser for help on using the repository browser.