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

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

Add IPC_M_STATE_CHANGE_AUTHORIZE.

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