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
RevLine 
[2d5a54f3]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
[2d5a54f3]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
[cc73a8a1]29/** @addtogroup genericipc
[b45c443]30 * @{
31 */
32/** @file
33 */
34
[2d5a54f3]35#include <arch.h>
36#include <proc/task.h>
[e3c762cd]37#include <proc/thread.h>
[2d5a54f3]38#include <errno.h>
39#include <memstr.h>
40#include <debug.h>
41#include <ipc/ipc.h>
[c0699467]42#include <abi/ipc/methods.h>
[2d5a54f3]43#include <ipc/sysipc.h>
[162f919]44#include <ipc/irq.h>
[4e49572]45#include <ipc/ipcrsc.h>
[fdd4898]46#include <ipc/event.h>
[e028660]47#include <ipc/kbox.h>
[057d21a]48#include <synch/waitq.h>
[9a1b20c]49#include <udebug/udebug_ipc.h>
[5626277]50#include <arch/interrupt.h>
[e3c762cd]51#include <syscall/copy.h>
[2bb8648]52#include <security/cap.h>
[6b10dab]53#include <console/console.h>
[7c23af9]54#include <mm/as.h>
[253f35a1]55#include <print.h>
[e2ab36f1]56#include <macros.h>
[2d5a54f3]57
[36d852c]58/**
59 * Maximum buffer size allowed for IPC_M_DATA_WRITE and IPC_M_DATA_READ
60 * requests.
61 */
[da1bafb]62#define DATA_XFER_LIMIT (64 * 1024)
63
64#define STRUCT_TO_USPACE(dst, src) copy_to_uspace((dst), (src), sizeof(*(src)))
[7918fce]65
[c9fff17]66/** Get phone from the current task by ID.
67 *
[da1bafb]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 *
[c9fff17]73 */
[96b02eb9]74static int phone_get(sysarg_t phoneid, phone_t **phone)
[c9fff17]75{
76 if (phoneid >= IPC_MAX_PHONES)
77 return EINVAL;
[da1bafb]78
[c9fff17]79 *phone = &TASK->phones[phoneid];
80 return EOK;
[ba81cab]81}
82
[228e490]83/** Decide if the interface and method is a system method.
[8b243f2]84 *
[228e490]85 * @param imethod Interface and method to be decided.
[da1bafb]86 *
[228e490]87 * @return True if the interface and method is a system
88 * interface and method.
[8b243f2]89 *
90 */
[228e490]91static inline bool method_is_system(sysarg_t imethod)
[2ba7810]92{
[228e490]93 if (imethod <= IPC_M_LAST_SYSTEM)
[da1bafb]94 return true;
95
96 return false;
[2ba7810]97}
98
[228e490]99/** Decide if the message with this interface and method is forwardable.
[2ba7810]100 *
[228e490]101 * Some system messages may be forwarded, for some of them
102 * it is useless.
[8b243f2]103 *
[228e490]104 * @param imethod Interface and method to be decided.
[da1bafb]105 *
[228e490]106 * @return True if the interface and method is forwardable.
[8b243f2]107 *
[2ba7810]108 */
[228e490]109static inline bool method_is_forwardable(sysarg_t imethod)
[2ba7810]110{
[228e490]111 switch (imethod) {
[2c0e5d2]112 case IPC_M_CONNECTION_CLONE:
[6aae539d]113 case IPC_M_CLONE_ESTABLISH:
[7918fce]114 case IPC_M_PHONE_HUNGUP:
115 /* This message is meant only for the original recipient. */
[da1bafb]116 return false;
[7918fce]117 default:
[da1bafb]118 return true;
[7918fce]119 }
[2ba7810]120}
121
[228e490]122/** Decide if the message with this interface and method is immutable on forward.
[0dc4258]123 *
[228e490]124 * Some system messages may be forwarded but their content cannot be altered.
[0dc4258]125 *
[228e490]126 * @param imethod Interface and method to be decided.
[da1bafb]127 *
[228e490]128 * @return True if the interface and method is immutable on forward.
[0dc4258]129 *
130 */
[228e490]131static inline bool method_is_immutable(sysarg_t imethod)
[0dc4258]132{
[228e490]133 switch (imethod) {
[27d293a]134 case IPC_M_SHARE_OUT:
135 case IPC_M_SHARE_IN:
[36d852c]136 case IPC_M_DATA_WRITE:
137 case IPC_M_DATA_READ:
[fdd4898]138 case IPC_M_STATE_CHANGE_AUTHORIZE:
[da1bafb]139 return true;
[0dc4258]140 default:
[da1bafb]141 return false;
[0dc4258]142 }
143}
144
[2d5a54f3]145
[8b243f2]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 *
[da1bafb]153 * @param call Call structure to be decided.
154 *
155 * @return true if the old call contents should be saved.
[8b243f2]156 *
[2d5a54f3]157 */
[da1bafb]158static inline bool answer_need_old(call_t *call)
[2d5a54f3]159{
[228e490]160 switch (IPC_GET_IMETHOD(call->data)) {
[2c0e5d2]161 case IPC_M_CONNECTION_CLONE:
[6aae539d]162 case IPC_M_CLONE_ESTABLISH:
[7918fce]163 case IPC_M_CONNECT_TO_ME:
164 case IPC_M_CONNECT_ME_TO:
[27d293a]165 case IPC_M_SHARE_OUT:
166 case IPC_M_SHARE_IN:
[36d852c]167 case IPC_M_DATA_WRITE:
168 case IPC_M_DATA_READ:
[fdd4898]169 case IPC_M_STATE_CHANGE_AUTHORIZE:
[da1bafb]170 return true;
[7918fce]171 default:
[da1bafb]172 return false;
[7918fce]173 }
[2d5a54f3]174}
175
[7975433]176static void cleanup_m_connection_clone(call_t *answer, ipc_data_t *olddata)
[9956fad9]177{
178 int phoneid = (int) IPC_GET_ARG1(*olddata);
179 phone_t *phone = &TASK->phones[phoneid];
180
[7975433]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{
[9956fad9]199 if (IPC_GET_RETVAL(answer->data) != EOK) {
200 /*
[7975433]201 * The recipient of the cloned phone rejected the offer.
[9956fad9]202 */
[7975433]203 cleanup_m_connection_clone(answer, olddata);
[9956fad9]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 /*
[7975433]215 * The other party on the cloned phone rejected our request
[9956fad9]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
[7975433]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
[9956fad9]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 */
[7975433]245 cleanup_m_connect_to_me(answer, olddata);
[9956fad9]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
[7975433]256static void cleanup_m_connect_me_to(call_t *answer, ipc_data_t *olddata)
257{
258 phone_dealloc(answer->priv);
259}
260
[9956fad9]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
[7975433]265 /* If the user accepted call, connect */
[9956fad9]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
[7975433]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
[8b243f2]448/** Interpret process answer as control information.
449 *
450 * This function is called directly after sys_ipc_answer().
[46fc2f9]451 *
[da1bafb]452 * @param answer Call structure with the answer.
453 * @param olddata Saved data of the request.
454 *
[9956fad9]455 * @return Return EOK on success or a negative error code.
[8b243f2]456 *
[46fc2f9]457 */
[9956fad9]458static int answer_preprocess(call_t *answer, ipc_data_t *olddata)
[2d5a54f3]459{
[9956fad9]460 int rc = EOK;
461
[2405bb5]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);
[7975433]468 cleanup_forgotten(answer, olddata);
[2405bb5]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
[6c441cf8]479 if ((native_t) IPC_GET_RETVAL(answer->data) == EHANGUP) {
[ca687ad]480 /* In case of forward, hangup the forwared phone,
481 * not the originator
482 */
[ff48a15]483 mutex_lock(&answer->data.phone->lock);
[da1bafb]484 irq_spinlock_lock(&TASK->answerbox.lock, true);
[eb3d379]485 if (answer->data.phone->state == IPC_PHONE_CONNECTED) {
[c4e4507]486 list_remove(&answer->data.phone->link);
[eb3d379]487 answer->data.phone->state = IPC_PHONE_SLAMMED;
[ca687ad]488 }
[da1bafb]489 irq_spinlock_unlock(&TASK->answerbox.lock, true);
[ff48a15]490 mutex_unlock(&answer->data.phone->lock);
[9f22213]491 }
[da1bafb]492
[2405bb5]493 if (!olddata) {
494 task_release(answer->sender);
[9956fad9]495 return rc;
[2405bb5]496 }
[da1bafb]497
[9956fad9]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;
[9a82ac1]526 default:
527 break;
[2d5a54f3]528 }
[da1bafb]529
[2405bb5]530 task_release(answer->sender);
531
[9956fad9]532 return rc;
[2d5a54f3]533}
534
[bf1fb9f]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);
[da1bafb]543 } else
[bf1fb9f]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
[924c2530]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
[8b243f2]690/** Called before the request is sent.
691 *
[da1bafb]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.
[7c7aae16]696 *
697 */
[9a1b20c]698static int request_preprocess(call_t *call, phone_t *phone)
[7c7aae16]699{
[924c2530]700 int rc = EOK;
701
[228e490]702 switch (IPC_GET_IMETHOD(call->data)) {
[924c2530]703 case IPC_M_CONNECTION_CLONE:
704 rc = r_preprocess_m_connection_clone(call, phone);
[2c0e5d2]705 break;
[6aae539d]706 case IPC_M_CLONE_ESTABLISH:
[924c2530]707 rc = r_preprocess_m_clone_establish(call, phone);
[2c0e5d2]708 break;
[924c2530]709 case IPC_M_CONNECT_ME_TO:
710 rc = r_preprocess_m_connect_me_to(call, phone);
[7c7aae16]711 break;
[924c2530]712 case IPC_M_SHARE_OUT:
713 rc = r_preprocess_m_share_out(call, phone);
[7c23af9]714 break;
[924c2530]715 case IPC_M_DATA_READ:
716 rc = r_preprocess_m_data_read(call, phone);
[a55d5f9f]717 break;
[924c2530]718 case IPC_M_DATA_WRITE:
719 rc = r_preprocess_m_data_write(call, phone);
[7918fce]720 break;
[924c2530]721 case IPC_M_STATE_CHANGE_AUTHORIZE:
722 rc = r_preprocess_m_state_change_authorize(call, phone);
[fdd4898]723 break;
[9a1b20c]724#ifdef CONFIG_UDEBUG
[79ae36dd]725 case IPC_M_DEBUG:
[924c2530]726 rc = udebug_request_preprocess(call, phone);
727 break;
[9a1b20c]728#endif
[7c7aae16]729 default:
730 break;
731 }
[da1bafb]732
[924c2530]733 return rc;
[7c7aae16]734}
735
[8b243f2]736/*******************************************************************************
737 * Functions called to process received call/answer before passing it to uspace.
738 *******************************************************************************/
[2d5a54f3]739
[8b243f2]740/** Do basic kernel processing of received call answer.
741 *
[da1bafb]742 * @param call Call structure with the answer.
743 *
[8b243f2]744 */
[7c7aae16]745static void process_answer(call_t *call)
[2d5a54f3]746{
[6c441cf8]747 if (((native_t) IPC_GET_RETVAL(call->data) == EHANGUP) &&
[51ec40f]748 (call->flags & IPC_CALL_FORWARDED))
[9f22213]749 IPC_SET_RETVAL(call->data, EFORWARD);
[da1bafb]750
[7c7aae16]751 if (call->flags & IPC_CALL_CONN_ME_TO) {
752 if (IPC_GET_RETVAL(call->data))
[0c1a5d8a]753 phone_dealloc(call->priv);
[7c7aae16]754 else
[b61d47d]755 IPC_SET_ARG5(call->data, call->priv);
[7c7aae16]756 }
[da1bafb]757
[a55d5f9f]758 if (call->buffer) {
[da1bafb]759 /*
760 * This must be an affirmative answer to IPC_M_DATA_READ
[79ae36dd]761 * or IPC_M_DEBUG/UDEBUG_M_MEM_READ...
[da1bafb]762 *
763 */
[a55d5f9f]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 }
[2d5a54f3]772}
773
[691d8d8]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
[8b243f2]794/** Do basic kernel processing of received call request.
795 *
[da1bafb]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.
[2d5a54f3]801 *
802 */
[51ec40f]803static int process_request(answerbox_t *box, call_t *call)
[2d5a54f3]804{
[691d8d8]805 int rc = EOK;
806
[228e490]807 switch (IPC_GET_IMETHOD(call->data)) {
[691d8d8]808 case IPC_M_CONNECT_TO_ME:
809 rc = r_process_m_connect_to_me(box, call);
810 break;
[79ae36dd]811 case IPC_M_DEBUG:
[691d8d8]812 rc = r_process_m_debug(box, call);
813 break;
[9a1b20c]814 default:
815 break;
816 }
[da1bafb]817
[691d8d8]818 return rc;
[2d5a54f3]819}
820
[97d17fe]821/** Check that the task did not exceed the allowed limit of asynchronous calls
822 * made over a phone.
[2d5a54f3]823 *
[228e490]824 * @param phone Phone to check the limit against.
825 *
[da1bafb]826 * @return 0 if limit not reached or -1 if limit exceeded.
827 *
[2d5a54f3]828 */
[97d17fe]829static int check_call_limit(phone_t *phone)
[2d5a54f3]830{
[97d17fe]831 if (atomic_get(&phone->active_calls) >= IPC_MAX_ASYNC_CALLS)
[2d5a54f3]832 return -1;
[da1bafb]833
[2d5a54f3]834 return 0;
835}
836
[8b243f2]837/** Make a fast asynchronous call over IPC.
[2d5a54f3]838 *
[3209923]839 * This function can only handle four arguments of payload, but is faster than
840 * the generic function sys_ipc_call_async_slow().
[8b243f2]841 *
[da1bafb]842 * @param phoneid Phone handle for the call.
[228e490]843 * @param imethod Interface and method of the call.
[da1bafb]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 *
[2d5a54f3]854 */
[228e490]855sysarg_t sys_ipc_call_async_fast(sysarg_t phoneid, sysarg_t imethod,
[96b02eb9]856 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
[2d5a54f3]857{
[da1bafb]858 phone_t *phone;
[c9fff17]859 if (phone_get(phoneid, &phone) != EOK)
860 return IPC_CALLRET_FATAL;
[228e490]861
[97d17fe]862 if (check_call_limit(phone))
863 return IPC_CALLRET_TEMPORARY;
[da1bafb]864
865 call_t *call = ipc_call_alloc(0);
[228e490]866 IPC_SET_IMETHOD(call->data, imethod);
[2d5a54f3]867 IPC_SET_ARG1(call->data, arg1);
868 IPC_SET_ARG2(call->data, arg2);
[3209923]869 IPC_SET_ARG3(call->data, arg3);
870 IPC_SET_ARG4(call->data, arg4);
[da1bafb]871
[8498915]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);
[da1bafb]877
878 int res = request_preprocess(call, phone);
879
880 if (!res)
[7c7aae16]881 ipc_call(phone, call);
882 else
883 ipc_backsend_err(phone, call, res);
[da1bafb]884
[96b02eb9]885 return (sysarg_t) call;
[2d5a54f3]886}
887
[8b243f2]888/** Make an asynchronous IPC call allowing to transmit the entire payload.
889 *
[da1bafb]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().
[2d5a54f3]894 *
895 */
[96b02eb9]896sysarg_t sys_ipc_call_async_slow(sysarg_t phoneid, ipc_data_t *data)
[2d5a54f3]897{
[da1bafb]898 phone_t *phone;
[c9fff17]899 if (phone_get(phoneid, &phone) != EOK)
900 return IPC_CALLRET_FATAL;
[4e49572]901
[97d17fe]902 if (check_call_limit(phone))
903 return IPC_CALLRET_TEMPORARY;
904
[da1bafb]905 call_t *call = ipc_call_alloc(0);
906 int rc = copy_from_uspace(&call->data.args, &data->args,
[51ec40f]907 sizeof(call->data.args));
[f58af46]908 if (rc != 0) {
909 ipc_call_free(call);
[96b02eb9]910 return (sysarg_t) rc;
[f58af46]911 }
[da1bafb]912
913 int res = request_preprocess(call, phone);
914
915 if (!res)
[7c7aae16]916 ipc_call(phone, call);
917 else
918 ipc_backsend_err(phone, call, res);
[da1bafb]919
[96b02eb9]920 return (sysarg_t) call;
[2d5a54f3]921}
922
[da1bafb]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.
[228e490]929 * @param imethod New interface and method to use for the forwarded call.
[da1bafb]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 *
[2ba7810]944 */
[96b02eb9]945static sysarg_t sys_ipc_forward_common(sysarg_t callid, sysarg_t phoneid,
[228e490]946 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
[96b02eb9]947 sysarg_t arg4, sysarg_t arg5, unsigned int mode, bool slow)
[2ba7810]948{
[da1bafb]949 call_t *call = get_call(callid);
[2ba7810]950 if (!call)
951 return ENOENT;
[48daf64]952
[9f22213]953 call->flags |= IPC_CALL_FORWARDED;
[da1bafb]954
955 phone_t *phone;
[c9fff17]956 if (phone_get(phoneid, &phone) != EOK) {
[2ba7810]957 IPC_SET_RETVAL(call->data, EFORWARD);
958 ipc_answer(&TASK->answerbox, call);
959 return ENOENT;
[c9fff17]960 }
[da1bafb]961
[228e490]962 if (!method_is_forwardable(IPC_GET_IMETHOD(call->data))) {
[2ba7810]963 IPC_SET_RETVAL(call->data, EFORWARD);
964 ipc_answer(&TASK->answerbox, call);
965 return EPERM;
966 }
[da1bafb]967
[0dc4258]968 /*
[4e5dabf]969 * User space is not allowed to change interface and method of system
[228e490]970 * methods on forward, allow changing ARG1, ARG2, ARG3 and ARG4 by
[4e5dabf]971 * means of imethod, arg1, arg2 and arg3.
[228e490]972 * If the interface and method is immutable, don't change anything.
[2ba7810]973 */
[228e490]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)
[b61d47d]977 phone_dealloc(IPC_GET_ARG5(call->data));
[da1bafb]978
[228e490]979 IPC_SET_ARG1(call->data, imethod);
[0dc4258]980 IPC_SET_ARG2(call->data, arg1);
[b61d47d]981 IPC_SET_ARG3(call->data, arg2);
[da1bafb]982
[4e5dabf]983 if (slow)
[48daf64]984 IPC_SET_ARG4(call->data, arg3);
[4e5dabf]985
986 /*
987 * For system methods we deliberately don't
988 * overwrite ARG5.
989 */
[0dc4258]990 } else {
[228e490]991 IPC_SET_IMETHOD(call->data, imethod);
[0dc4258]992 IPC_SET_ARG1(call->data, arg1);
[b61d47d]993 IPC_SET_ARG2(call->data, arg2);
[48daf64]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 }
[0dc4258]999 }
[2ba7810]1000 }
[da1bafb]1001
[d40a8ff]1002 return ipc_forward(call, phone, &TASK->answerbox, mode);
[2ba7810]1003}
1004
[48daf64]1005/** Forward a received call to another destination - fast version.
1006 *
[228e490]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.
[da1bafb]1013 *
1014 * @param callid Hash of the call to forward.
1015 * @param phoneid Phone handle to use for forwarding.
[228e490]1016 * @param imethod New interface and method to use for the forwarded call.
[da1bafb]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 *
[48daf64]1023 */
[96b02eb9]1024sysarg_t sys_ipc_forward_fast(sysarg_t callid, sysarg_t phoneid,
[228e490]1025 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
[48daf64]1026{
[228e490]1027 return sys_ipc_forward_common(callid, phoneid, imethod, arg1, arg2, 0, 0,
[48daf64]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.
[228e490]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.
[da1bafb]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 *
[48daf64]1047 */
[96b02eb9]1048sysarg_t sys_ipc_forward_slow(sysarg_t callid, sysarg_t phoneid,
[da1bafb]1049 ipc_data_t *data, unsigned int mode)
[48daf64]1050{
1051 ipc_data_t newdata;
[da1bafb]1052 int rc = copy_from_uspace(&newdata.args, &data->args,
[48daf64]1053 sizeof(newdata.args));
[da1bafb]1054 if (rc != 0)
[96b02eb9]1055 return (sysarg_t) rc;
[da1bafb]1056
[48daf64]1057 return sys_ipc_forward_common(callid, phoneid,
[228e490]1058 IPC_GET_IMETHOD(newdata), IPC_GET_ARG1(newdata),
[48daf64]1059 IPC_GET_ARG2(newdata), IPC_GET_ARG3(newdata),
1060 IPC_GET_ARG4(newdata), IPC_GET_ARG5(newdata), mode, true);
1061}
1062
[8b243f2]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 *
[da1bafb]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.
[8b243f2]1076 *
1077 */
[96b02eb9]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)
[2d5a54f3]1080{
[897f2e76]1081 /* Do not answer notification callids */
1082 if (callid & IPC_CALLID_NOTIFICATION)
1083 return 0;
[da1bafb]1084
1085 call_t *call = get_call(callid);
[2ba7810]1086 if (!call)
1087 return ENOENT;
[da1bafb]1088
1089 ipc_data_t saved_data;
1090 bool saved;
1091
[9f22213]1092 if (answer_need_old(call)) {
[2ba7810]1093 memcpy(&saved_data, &call->data, sizeof(call->data));
[da1bafb]1094 saved = true;
1095 } else
1096 saved = false;
1097
[2d5a54f3]1098 IPC_SET_RETVAL(call->data, retval);
1099 IPC_SET_ARG1(call->data, arg1);
1100 IPC_SET_ARG2(call->data, arg2);
[b74959bd]1101 IPC_SET_ARG3(call->data, arg3);
1102 IPC_SET_ARG4(call->data, arg4);
[da1bafb]1103
[8498915]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);
[da1bafb]1109 int rc = answer_preprocess(call, saved ? &saved_data : NULL);
1110
[2d5a54f3]1111 ipc_answer(&TASK->answerbox, call);
[7c23af9]1112 return rc;
[2d5a54f3]1113}
1114
[8b243f2]1115/** Answer an IPC call.
1116 *
[da1bafb]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.
[8b243f2]1121 *
1122 */
[96b02eb9]1123sysarg_t sys_ipc_answer_slow(sysarg_t callid, ipc_data_t *data)
[2d5a54f3]1124{
[897f2e76]1125 /* Do not answer notification callids */
1126 if (callid & IPC_CALLID_NOTIFICATION)
1127 return 0;
[da1bafb]1128
1129 call_t *call = get_call(callid);
[2ba7810]1130 if (!call)
1131 return ENOENT;
[da1bafb]1132
1133 ipc_data_t saved_data;
1134 bool saved;
1135
[9f22213]1136 if (answer_need_old(call)) {
[2ba7810]1137 memcpy(&saved_data, &call->data, sizeof(call->data));
[da1bafb]1138 saved = true;
1139 } else
1140 saved = false;
1141
1142 int rc = copy_from_uspace(&call->data.args, &data->args,
[51ec40f]1143 sizeof(call->data.args));
[e3c762cd]1144 if (rc != 0)
1145 return rc;
[da1bafb]1146
1147 rc = answer_preprocess(call, saved ? &saved_data : NULL);
[2d5a54f3]1148
1149 ipc_answer(&TASK->answerbox, call);
[7c23af9]1150 return rc;
[2d5a54f3]1151}
1152
[8b243f2]1153/** Hang up a phone.
[2d5a54f3]1154 *
[da1bafb]1155 * @param Phone handle of the phone to be hung up.
1156 *
1157 * @return 0 on success or an error code.
[8b243f2]1158 *
[fbcfd458]1159 */
[96b02eb9]1160sysarg_t sys_ipc_hangup(sysarg_t phoneid)
[fbcfd458]1161{
1162 phone_t *phone;
[da1bafb]1163
[c9fff17]1164 if (phone_get(phoneid, &phone) != EOK)
1165 return ENOENT;
[da1bafb]1166
[d8f7362]1167 if (ipc_phone_hangup(phone))
[fbcfd458]1168 return -1;
[da1bafb]1169
[fbcfd458]1170 return 0;
1171}
1172
[8b243f2]1173/** Wait for an incoming IPC call or an answer.
[fbcfd458]1174 *
[da1bafb]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.
[bd5a663]1184 *
[2d5a54f3]1185 */
[96b02eb9]1186sysarg_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec,
[da1bafb]1187 unsigned int flags)
[2d5a54f3]1188{
1189 call_t *call;
[da1bafb]1190
[741fd16]1191restart:
[da1bafb]1192
[741fd16]1193#ifdef CONFIG_UDEBUG
1194 udebug_stoppable_begin();
[da1bafb]1195#endif
1196
[51ec40f]1197 call = ipc_wait_for_call(&TASK->answerbox, usec,
1198 flags | SYNCH_FLAGS_INTERRUPTIBLE);
[da1bafb]1199
[741fd16]1200#ifdef CONFIG_UDEBUG
1201 udebug_stoppable_end();
1202#endif
[da1bafb]1203
[9f22213]1204 if (!call)
1205 return 0;
[da1bafb]1206
[5626277]1207 if (call->flags & IPC_CALL_NOTIF) {
[43752b6]1208 /* Set in_phone_hash to the interrupt counter */
[0c1a5d8a]1209 call->data.phone = (void *) call->priv;
[43752b6]1210
1211 STRUCT_TO_USPACE(calldata, &call->data);
[da1bafb]1212
[5626277]1213 ipc_call_free(call);
1214
[96b02eb9]1215 return ((sysarg_t) call) | IPC_CALLID_NOTIFICATION;
[5626277]1216 }
[da1bafb]1217
[2d5a54f3]1218 if (call->flags & IPC_CALL_ANSWERED) {
[7c7aae16]1219 process_answer(call);
[da1bafb]1220
[fbcfd458]1221 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
1222 ipc_call_free(call);
1223 goto restart;
1224 }
[da1bafb]1225
[fbcfd458]1226 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
[2d5a54f3]1227 ipc_call_free(call);
[da1bafb]1228
[96b02eb9]1229 return ((sysarg_t) call) | IPC_CALLID_ANSWERED;
[2d5a54f3]1230 }
[da1bafb]1231
[2d5a54f3]1232 if (process_request(&TASK->answerbox, call))
1233 goto restart;
[da1bafb]1234
[7c7aae16]1235 /* Include phone address('id') of the caller in the request,
1236 * copy whole call->data, not only call->data.args */
[bd55bbb]1237 if (STRUCT_TO_USPACE(calldata, &call->data)) {
[e06da7e]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.
[b11ee88]1241 */
[e06da7e]1242 ipc_data_t saved_data;
[da1bafb]1243 bool saved;
1244
[e06da7e]1245 if (answer_need_old(call)) {
1246 memcpy(&saved_data, &call->data, sizeof(call->data));
[da1bafb]1247 saved = true;
1248 } else
1249 saved = false;
[e06da7e]1250
1251 IPC_SET_RETVAL(call->data, EPARTY);
[da1bafb]1252 (void) answer_preprocess(call, saved ? &saved_data : NULL);
[e06da7e]1253 ipc_answer(&TASK->answerbox, call);
[bd55bbb]1254 return 0;
1255 }
[da1bafb]1256
[96b02eb9]1257 return (sysarg_t) call;
[2d5a54f3]1258}
[5626277]1259
[da1bafb]1260/** Interrupt one thread from sys_ipc_wait_for_call().
1261 *
1262 */
[96b02eb9]1263sysarg_t sys_ipc_poke(void)
[057d21a]1264{
[da1bafb]1265 waitq_unsleep(&TASK->answerbox.wq);
[057d21a]1266 return EOK;
1267}
1268
[8b243f2]1269/** Connect an IRQ handler to a task.
[2b017ba]1270 *
[228e490]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.
[da1bafb]1275 *
1276 * @return EPERM or a return code returned by ipc_irq_register().
[2b017ba]1277 *
1278 */
[f044e96]1279sysarg_t sys_irq_register(inr_t inr, devno_t devno, sysarg_t imethod,
[51ec40f]1280 irq_code_t *ucode)
[5626277]1281{
[2bb8648]1282 if (!(cap_get(TASK) & CAP_IRQ_REG))
1283 return EPERM;
[da1bafb]1284
[228e490]1285 return ipc_irq_register(&TASK->answerbox, inr, devno, imethod, ucode);
[5626277]1286}
1287
[8b243f2]1288/** Disconnect an IRQ handler from a task.
1289 *
[da1bafb]1290 * @param inr IRQ number.
1291 * @param devno Device number.
1292 *
1293 * @return Zero on success or EPERM on error.
[2b017ba]1294 *
1295 */
[f044e96]1296sysarg_t sys_irq_unregister(inr_t inr, devno_t devno)
[5626277]1297{
[2bb8648]1298 if (!(cap_get(TASK) & CAP_IRQ_REG))
1299 return EPERM;
[da1bafb]1300
[2b017ba]1301 ipc_irq_unregister(&TASK->answerbox, inr, devno);
[da1bafb]1302
[5626277]1303 return 0;
1304}
[b45c443]1305
[6b10dab]1306#ifdef __32_BITS__
[9a1b20c]1307
[6b10dab]1308/** Syscall connect to a task by ID (32 bits)
[da1bafb]1309 *
1310 * @return Phone id on success, or negative error code.
[9a1b20c]1311 *
1312 */
[6b10dab]1313sysarg_t sys_ipc_connect_kbox(sysarg64_t *uspace_taskid)
[9a1b20c]1314{
1315#ifdef CONFIG_UDEBUG
[6b10dab]1316 sysarg64_t taskid;
1317 int rc = copy_from_uspace(&taskid, uspace_taskid, sizeof(sysarg64_t));
[9a1b20c]1318 if (rc != 0)
[96b02eb9]1319 return (sysarg_t) rc;
[da1bafb]1320
[6b10dab]1321 return ipc_connect_kbox((task_id_t) taskid);
[9a1b20c]1322#else
[96b02eb9]1323 return (sysarg_t) ENOTSUP;
[9a1b20c]1324#endif
1325}
1326
[6b10dab]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
[cc73a8a1]1347/** @}
[b45c443]1348 */
Note: See TracBrowser for help on using the repository browser.