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

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

Use user-defined labels instead of phone hashes

This commit changes the way how the async framework maps incomming calls
to connections. Instead of abusing the kernel addresses of attached
phones as identifiers, the IPC_M_CONNECT_TO_ME and IPC_M_CONNECT_ME_TO
messages allow the server to specify an arbitrary label which is
remembered in the connected phone and consequently imprinted on each
call which is routed through this phone.

The async framework uses the address of the connection structure as the
label. This removes the need for a connection hash table because each
incoming call already remembers the connection in its label.

To disambiguate this new label and the other user-defined label used for
answers, the call structure now has the request_label member for the
former and answer_label member for the latter.

This commit also moves the kernel definition of ipc_data_t to abi/ and
removes the uspace redefinition thereof. Finally, when forwarding the
IPC_M_CONNECT_TO_ME call, the phone capability and the kernel object
allocated in request_process are now correctly disposed of.

  • Property mode set to 100644
File size: 24.2 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 kernel_generic_ipc
30 * @{
31 */
32/** @file
33 */
34
35#include <arch.h>
36#include <assert.h>
37#include <errno.h>
38#include <mem.h>
39#include <ipc/ipc.h>
40#include <abi/ipc/methods.h>
41#include <ipc/sysipc.h>
42#include <ipc/sysipc_ops.h>
43#include <ipc/sysipc_priv.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 <arch/interrupt.h>
50#include <syscall/copy.h>
51#include <security/perm.h>
52#include <console/console.h>
53#include <print.h>
54#include <macros.h>
55#include <cap/cap.h>
56
57#define STRUCT_TO_USPACE(dst, src) copy_to_uspace((dst), (src), sizeof(*(src)))
58
59/** Decide if the interface and method is a system method.
60 *
61 * @param imethod Interface and method to be decided.
62 *
63 * @return True if the interface and method is a system
64 * interface and method.
65 *
66 */
67static inline bool method_is_system(sysarg_t imethod)
68{
69 if (imethod <= IPC_M_LAST_SYSTEM)
70 return true;
71
72 return false;
73}
74
75/** Decide if the message with this interface and method is forwardable.
76 *
77 * Some system messages may be forwarded, for some of them
78 * it is useless.
79 *
80 * @param imethod Interface and method to be decided.
81 *
82 * @return True if the interface and method is forwardable.
83 *
84 */
85static inline bool method_is_forwardable(sysarg_t imethod)
86{
87 switch (imethod) {
88 case IPC_M_PHONE_HUNGUP:
89 /* This message is meant only for the original recipient. */
90 return false;
91 default:
92 return true;
93 }
94}
95
96/** Decide if the message with this interface and method is immutable on forward.
97 *
98 * Some system messages may be forwarded but their content cannot be altered.
99 *
100 * @param imethod Interface and method to be decided.
101 *
102 * @return True if the interface and method is immutable on forward.
103 *
104 */
105static inline bool method_is_immutable(sysarg_t imethod)
106{
107 switch (imethod) {
108 case IPC_M_PAGE_IN:
109 case IPC_M_SHARE_OUT:
110 case IPC_M_SHARE_IN:
111 case IPC_M_DATA_WRITE:
112 case IPC_M_DATA_READ:
113 case IPC_M_STATE_CHANGE_AUTHORIZE:
114 return true;
115 default:
116 return false;
117 }
118}
119
120/*
121 * Functions that preprocess answer before sending it to the recepient.
122 */
123
124/** Decide if the caller (e.g. ipc_answer()) should save the old call contents
125 * for answer_preprocess().
126 *
127 * @param call Call structure to be decided.
128 *
129 * @return true if the old call contents should be saved.
130 *
131 */
132static inline bool answer_need_old(call_t *call)
133{
134 switch (IPC_GET_IMETHOD(call->data)) {
135 case IPC_M_CONNECT_TO_ME:
136 case IPC_M_CONNECT_ME_TO:
137 case IPC_M_PAGE_IN:
138 case IPC_M_SHARE_OUT:
139 case IPC_M_SHARE_IN:
140 case IPC_M_DATA_WRITE:
141 case IPC_M_DATA_READ:
142 case IPC_M_STATE_CHANGE_AUTHORIZE:
143 return true;
144 default:
145 return false;
146 }
147}
148
149/** Interpret process answer as control information.
150 *
151 * This function is called directly after sys_ipc_answer().
152 *
153 * @param answer Call structure with the answer.
154 * @param olddata Saved data of the request.
155 *
156 * @return Return EOK on success or an error code.
157 *
158 */
159errno_t answer_preprocess(call_t *answer, ipc_data_t *olddata)
160{
161 errno_t rc = EOK;
162
163 spinlock_lock(&answer->forget_lock);
164 if (answer->forget) {
165 /*
166 * This is a forgotten call and answer->sender is not valid.
167 */
168 spinlock_unlock(&answer->forget_lock);
169
170 SYSIPC_OP(answer_cleanup, answer, olddata);
171 return rc;
172 } else {
173 assert(answer->active);
174
175 /*
176 * Mark the call as inactive to prevent _ipc_answer_free_call()
177 * from attempting to remove the call from the active list
178 * itself.
179 */
180 answer->active = false;
181
182 /*
183 * Remove the call from the sender's active call list.
184 * We enforce this locking order so that any potential
185 * concurrently executing forget operation is forced to
186 * release its active_calls_lock and lose the race to
187 * forget this soon to be answered call.
188 */
189 spinlock_lock(&answer->sender->active_calls_lock);
190 list_remove(&answer->ta_link);
191 spinlock_unlock(&answer->sender->active_calls_lock);
192 }
193 spinlock_unlock(&answer->forget_lock);
194
195 if ((errno_t) IPC_GET_RETVAL(answer->data) == EHANGUP) {
196 phone_t *phone = answer->caller_phone;
197 mutex_lock(&phone->lock);
198 if (phone->state == IPC_PHONE_CONNECTED) {
199 irq_spinlock_lock(&phone->callee->lock, true);
200 list_remove(&phone->link);
201 /* Drop callee->connected_phones reference */
202 kobject_put(phone->kobject);
203 phone->state = IPC_PHONE_SLAMMED;
204 phone->label = 0;
205 irq_spinlock_unlock(&phone->callee->lock, true);
206 }
207 mutex_unlock(&phone->lock);
208 }
209
210 if (!olddata)
211 return rc;
212
213 return SYSIPC_OP(answer_preprocess, answer, olddata);
214}
215
216/** Called before the request is sent.
217 *
218 * @param call Call structure with the request.
219 * @param phone Phone that the call will be sent through.
220 *
221 * @return Return 0 on success, ELIMIT or EPERM on error.
222 *
223 */
224static errno_t request_preprocess(call_t *call, phone_t *phone)
225{
226 call->request_method = IPC_GET_IMETHOD(call->data);
227 return SYSIPC_OP(request_preprocess, call, phone);
228}
229
230/*
231 * Functions called to process received call/answer before passing it to uspace.
232 */
233
234/** Do basic kernel processing of received call answer.
235 *
236 * @param call Call structure with the answer.
237 *
238 */
239static void process_answer(call_t *call)
240{
241 if (((errno_t) IPC_GET_RETVAL(call->data) == EHANGUP) &&
242 (call->flags & IPC_CALL_FORWARDED))
243 IPC_SET_RETVAL(call->data, EFORWARD);
244
245 SYSIPC_OP(answer_process, call);
246}
247
248/** Do basic kernel processing of received call request.
249 *
250 * @param box Destination answerbox structure.
251 * @param call Call structure with the request.
252 *
253 * @return 0 if the call should be passed to userspace.
254 * @return -1 if the call should be ignored.
255 *
256 */
257static int process_request(answerbox_t *box, call_t *call)
258{
259 return SYSIPC_OP(request_process, call, box);
260}
261
262/** Make a call over IPC and wait for reply.
263 *
264 * @param handle Phone capability handle for the call.
265 * @param data[inout] Structure with request/reply data.
266 * @param priv Value to be stored in call->priv.
267 *
268 * @return EOK on success.
269 * @return ENOENT if there is no such phone handle.
270 *
271 */
272errno_t
273ipc_req_internal(cap_phone_handle_t handle, ipc_data_t *data, sysarg_t priv)
274{
275 kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
276 if (!kobj->phone)
277 return ENOENT;
278
279 call_t *call = ipc_call_alloc(0);
280 call->priv = priv;
281 memcpy(call->data.args, data->args, sizeof(data->args));
282
283 errno_t rc = request_preprocess(call, kobj->phone);
284 if (!rc) {
285#ifdef CONFIG_UDEBUG
286 udebug_stoppable_begin();
287#endif
288
289 kobject_add_ref(call->kobject);
290 rc = ipc_call_sync(kobj->phone, call);
291 spinlock_lock(&call->forget_lock);
292 bool forgotten = call->forget;
293 spinlock_unlock(&call->forget_lock);
294 kobject_put(call->kobject);
295
296#ifdef CONFIG_UDEBUG
297 udebug_stoppable_end();
298#endif
299
300 if (rc != EOK) {
301 if (!forgotten) {
302 /*
303 * There was an error, but it did not result
304 * in the call being forgotten. In fact, the
305 * call was not even sent. We are still
306 * its owners and are responsible for its
307 * deallocation.
308 */
309 kobject_put(call->kobject);
310 } else {
311 /*
312 * The call was forgotten and it changed hands.
313 * We are no longer expected to free it.
314 */
315 assert(rc == EINTR);
316 }
317 kobject_put(kobj);
318 return rc;
319 }
320
321 process_answer(call);
322 } else
323 IPC_SET_RETVAL(call->data, rc);
324
325 memcpy(data->args, call->data.args, sizeof(data->args));
326 kobject_put(call->kobject);
327 kobject_put(kobj);
328
329 return EOK;
330}
331
332/** Check that the task did not exceed the allowed limit of asynchronous calls
333 * made over a phone.
334 *
335 * @param phone Phone to check the limit against.
336 *
337 * @return 0 if limit not reached or -1 if limit exceeded.
338 *
339 */
340static int check_call_limit(phone_t *phone)
341{
342 if (atomic_load(&phone->active_calls) >= IPC_MAX_ASYNC_CALLS)
343 return -1;
344
345 return 0;
346}
347
348/** Make a fast asynchronous call over IPC.
349 *
350 * This function can only handle three arguments of payload, but is faster than
351 * the generic function sys_ipc_call_async_slow().
352 *
353 * @param handle Phone capability handle for the call.
354 * @param imethod Interface and method of the call.
355 * @param arg1 Service-defined payload argument.
356 * @param arg2 Service-defined payload argument.
357 * @param arg3 Service-defined payload argument.
358 * @param label User-defined label.
359 *
360 * @return EOK on success.
361 * @return An error code on error.
362 *
363 */
364sys_errno_t sys_ipc_call_async_fast(cap_phone_handle_t handle, sysarg_t imethod,
365 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t label)
366{
367 kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
368 if (!kobj)
369 return ENOENT;
370
371 if (check_call_limit(kobj->phone)) {
372 kobject_put(kobj);
373 return ELIMIT;
374 }
375
376 call_t *call = ipc_call_alloc(0);
377 IPC_SET_IMETHOD(call->data, imethod);
378 IPC_SET_ARG1(call->data, arg1);
379 IPC_SET_ARG2(call->data, arg2);
380 IPC_SET_ARG3(call->data, arg3);
381
382 /*
383 * To achieve deterministic behavior, zero out arguments that are beyond
384 * the limits of the fast version.
385 */
386 IPC_SET_ARG5(call->data, 0);
387
388 /* Set the user-defined label */
389 call->data.answer_label = label;
390
391 errno_t res = request_preprocess(call, kobj->phone);
392
393 if (!res)
394 ipc_call(kobj->phone, call);
395 else
396 ipc_backsend_err(kobj->phone, call, res);
397
398 kobject_put(kobj);
399 return EOK;
400}
401
402/** Make an asynchronous IPC call allowing to transmit the entire payload.
403 *
404 * @param handle Phone capability for the call.
405 * @param data Userspace address of call data with the request.
406 * @param label User-defined label.
407 *
408 * @return See sys_ipc_call_async_fast().
409 *
410 */
411sys_errno_t sys_ipc_call_async_slow(cap_phone_handle_t handle, ipc_data_t *data,
412 sysarg_t label)
413{
414 kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
415 if (!kobj)
416 return ENOENT;
417
418 if (check_call_limit(kobj->phone)) {
419 kobject_put(kobj);
420 return ELIMIT;
421 }
422
423 call_t *call = ipc_call_alloc(0);
424 errno_t rc = copy_from_uspace(&call->data.args, &data->args,
425 sizeof(call->data.args));
426 if (rc != EOK) {
427 kobject_put(call->kobject);
428 kobject_put(kobj);
429 return (sys_errno_t) rc;
430 }
431
432 /* Set the user-defined label */
433 call->data.answer_label = label;
434
435 errno_t res = request_preprocess(call, kobj->phone);
436
437 if (!res)
438 ipc_call(kobj->phone, call);
439 else
440 ipc_backsend_err(kobj->phone, call, res);
441
442 kobject_put(kobj);
443 return EOK;
444}
445
446/** Forward a received call to another destination
447 *
448 * Common code for both the fast and the slow version.
449 *
450 * @param chandle Call handle of the forwarded call.
451 * @param phandle Phone handle to use for forwarding.
452 * @param imethod New interface and method to use for the forwarded call.
453 * @param arg1 New value of the first argument for the forwarded call.
454 * @param arg2 New value of the second argument for the forwarded call.
455 * @param arg3 New value of the third argument for the forwarded call.
456 * @param arg4 New value of the fourth argument for the forwarded call.
457 * @param arg5 New value of the fifth argument for the forwarded call.
458 * @param mode Flags that specify mode of the forward operation.
459 * @param slow If true, arg3, arg4 and arg5 are considered. Otherwise
460 * the function considers only the fast version arguments:
461 * i.e. arg1 and arg2.
462 *
463 * @return 0 on succes, otherwise an error code.
464 *
465 * Warning: Make sure that ARG5 is not rewritten for certain system IPC
466 *
467 */
468static sys_errno_t sys_ipc_forward_common(cap_call_handle_t chandle,
469 cap_phone_handle_t phandle, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2,
470 sysarg_t arg3, sysarg_t arg4, sysarg_t arg5, unsigned int mode, bool slow)
471{
472 kobject_t *ckobj = cap_unpublish(TASK, chandle, KOBJECT_TYPE_CALL);
473 if (!ckobj)
474 return ENOENT;
475
476 call_t *call = ckobj->call;
477
478 ipc_data_t old;
479 bool need_old = answer_need_old(call);
480 if (need_old)
481 old = call->data;
482
483 bool after_forward = false;
484 errno_t rc;
485
486 kobject_t *pkobj = kobject_get(TASK, phandle, KOBJECT_TYPE_PHONE);
487 if (!pkobj) {
488 rc = ENOENT;
489 goto error;
490 }
491
492 if (!method_is_forwardable(IPC_GET_IMETHOD(call->data))) {
493 rc = EPERM;
494 goto error;
495 }
496
497 call->flags |= IPC_CALL_FORWARDED;
498
499 /*
500 * User space is not allowed to change interface and method of system
501 * methods on forward, allow changing ARG1, ARG2, ARG3 and ARG4 by
502 * means of imethod, arg1, arg2 and arg3.
503 * If the interface and method is immutable, don't change anything.
504 */
505 if (!method_is_immutable(IPC_GET_IMETHOD(call->data))) {
506 if (method_is_system(IPC_GET_IMETHOD(call->data))) {
507 if (IPC_GET_IMETHOD(call->data) ==
508 IPC_M_CONNECT_TO_ME) {
509 kobject_put((kobject_t *) call->priv);
510 call->priv = 0;
511 cap_free(TASK,
512 (cap_handle_t) IPC_GET_ARG5(call->data));
513 }
514
515 IPC_SET_ARG1(call->data, imethod);
516 IPC_SET_ARG2(call->data, arg1);
517 IPC_SET_ARG3(call->data, arg2);
518
519 if (slow)
520 IPC_SET_ARG4(call->data, arg3);
521
522 /*
523 * For system methods we deliberately don't
524 * overwrite ARG5.
525 */
526 } else {
527 IPC_SET_IMETHOD(call->data, imethod);
528 IPC_SET_ARG1(call->data, arg1);
529 IPC_SET_ARG2(call->data, arg2);
530 if (slow) {
531 IPC_SET_ARG3(call->data, arg3);
532 IPC_SET_ARG4(call->data, arg4);
533 IPC_SET_ARG5(call->data, arg5);
534 }
535 }
536 }
537
538 rc = ipc_forward(call, pkobj->phone, &TASK->answerbox, mode);
539 if (rc != EOK) {
540 after_forward = true;
541 goto error;
542 }
543
544 cap_free(TASK, chandle);
545 kobject_put(ckobj);
546 kobject_put(pkobj);
547 return EOK;
548
549error:
550 IPC_SET_RETVAL(call->data, EFORWARD);
551 (void) answer_preprocess(call, need_old ? &old : NULL);
552 if (after_forward)
553 _ipc_answer_free_call(call, false);
554 else
555 ipc_answer(&TASK->answerbox, call);
556
557 cap_free(TASK, chandle);
558 kobject_put(ckobj);
559
560 if (pkobj)
561 kobject_put(pkobj);
562 return rc;
563}
564
565/** Forward a received call to another destination - fast version.
566 *
567 * In case the original interface and method is a system method, ARG1, ARG2
568 * and ARG3 are overwritten in the forwarded message with the new method and
569 * the new arg1 and arg2, respectively. Otherwise the IMETHOD, ARG1 and ARG2
570 * are rewritten with the new interface and method, arg1 and arg2, respectively.
571 * Also note there is a set of immutable methods, for which the new method and
572 * arguments are not set and these values are ignored.
573 *
574 * @param chandle Call handle of the call to forward.
575 * @param phandle Phone handle to use for forwarding.
576 * @param imethod New interface and method to use for the forwarded call.
577 * @param arg1 New value of the first argument for the forwarded call.
578 * @param arg2 New value of the second argument for the forwarded call.
579 * @param mode Flags that specify mode of the forward operation.
580 *
581 * @return 0 on succes, otherwise an error code.
582 *
583 */
584sys_errno_t sys_ipc_forward_fast(cap_call_handle_t chandle,
585 cap_phone_handle_t phandle, sysarg_t imethod, sysarg_t arg1, sysarg_t arg2,
586 unsigned int mode)
587{
588 return sys_ipc_forward_common(chandle, phandle, imethod, arg1, arg2, 0,
589 0, 0, mode, false);
590}
591
592/** Forward a received call to another destination - slow version.
593 *
594 * This function is the slow verision of the sys_ipc_forward_fast interface.
595 * It can copy all five new arguments and the new interface and method from
596 * the userspace. It naturally extends the functionality of the fast version.
597 * For system methods, it additionally stores the new value of arg3 to ARG4.
598 * For non-system methods, it additionally stores the new value of arg3, arg4
599 * and arg5, respectively, to ARG3, ARG4 and ARG5, respectively.
600 *
601 * @param chandle Call handle of the call to forward.
602 * @param phandle Phone handle to use for forwarding.
603 * @param data Userspace address of the new IPC data.
604 * @param mode Flags that specify mode of the forward operation.
605 *
606 * @return 0 on succes, otherwise an error code.
607 *
608 */
609sys_errno_t sys_ipc_forward_slow(cap_call_handle_t chandle,
610 cap_phone_handle_t phandle, ipc_data_t *data, unsigned int mode)
611{
612 ipc_data_t newdata;
613 errno_t rc = copy_from_uspace(&newdata.args, &data->args,
614 sizeof(newdata.args));
615 if (rc != EOK)
616 return (sys_errno_t) rc;
617
618 return sys_ipc_forward_common(chandle, phandle,
619 IPC_GET_IMETHOD(newdata), IPC_GET_ARG1(newdata),
620 IPC_GET_ARG2(newdata), IPC_GET_ARG3(newdata),
621 IPC_GET_ARG4(newdata), IPC_GET_ARG5(newdata), mode, true);
622}
623
624/** Answer an IPC call - fast version.
625 *
626 * This function can handle only two return arguments of payload, but is faster
627 * than the generic sys_ipc_answer().
628 *
629 * @param chandle Call handle to be answered.
630 * @param retval Return value of the answer.
631 * @param arg1 Service-defined return value.
632 * @param arg2 Service-defined return value.
633 * @param arg3 Service-defined return value.
634 * @param arg4 Service-defined return value.
635 *
636 * @return 0 on success, otherwise an error code.
637 *
638 */
639sys_errno_t sys_ipc_answer_fast(cap_call_handle_t chandle, sysarg_t retval,
640 sysarg_t arg1, sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
641{
642 kobject_t *kobj = cap_unpublish(TASK, chandle, KOBJECT_TYPE_CALL);
643 if (!kobj)
644 return ENOENT;
645
646 call_t *call = kobj->call;
647 assert(!(call->flags & IPC_CALL_ANSWERED));
648
649 ipc_data_t saved_data;
650 bool saved;
651
652 if (answer_need_old(call)) {
653 memcpy(&saved_data, &call->data, sizeof(call->data));
654 saved = true;
655 } else
656 saved = false;
657
658 IPC_SET_RETVAL(call->data, retval);
659 IPC_SET_ARG1(call->data, arg1);
660 IPC_SET_ARG2(call->data, arg2);
661 IPC_SET_ARG3(call->data, arg3);
662 IPC_SET_ARG4(call->data, arg4);
663
664 /*
665 * To achieve deterministic behavior, zero out arguments that are beyond
666 * the limits of the fast version.
667 */
668 IPC_SET_ARG5(call->data, 0);
669 errno_t rc = answer_preprocess(call, saved ? &saved_data : NULL);
670
671 ipc_answer(&TASK->answerbox, call);
672
673 kobject_put(kobj);
674 cap_free(TASK, chandle);
675
676 return rc;
677}
678
679/** Answer an IPC call.
680 *
681 * @param chandle Call handle to be answered.
682 * @param data Userspace address of call data with the answer.
683 *
684 * @return 0 on success, otherwise an error code.
685 *
686 */
687sys_errno_t sys_ipc_answer_slow(cap_call_handle_t chandle, ipc_data_t *data)
688{
689 kobject_t *kobj = cap_unpublish(TASK, chandle, KOBJECT_TYPE_CALL);
690 if (!kobj)
691 return ENOENT;
692
693 call_t *call = kobj->call;
694 assert(!(call->flags & IPC_CALL_ANSWERED));
695
696 ipc_data_t saved_data;
697 bool saved;
698
699 if (answer_need_old(call)) {
700 memcpy(&saved_data, &call->data, sizeof(call->data));
701 saved = true;
702 } else
703 saved = false;
704
705 errno_t rc = copy_from_uspace(&call->data.args, &data->args,
706 sizeof(call->data.args));
707 if (rc != EOK) {
708 /*
709 * Republish the capability so that the call does not get lost.
710 */
711 cap_publish(TASK, chandle, kobj);
712 return rc;
713 }
714
715 rc = answer_preprocess(call, saved ? &saved_data : NULL);
716
717 ipc_answer(&TASK->answerbox, call);
718
719 kobject_put(kobj);
720 cap_free(TASK, chandle);
721
722 return rc;
723}
724
725/** Hang up a phone.
726 *
727 * @param handle Phone capability handle of the phone to be hung up.
728 *
729 * @return 0 on success or an error code.
730 *
731 */
732sys_errno_t sys_ipc_hangup(cap_phone_handle_t handle)
733{
734 kobject_t *kobj = cap_unpublish(TASK, handle, KOBJECT_TYPE_PHONE);
735 if (!kobj)
736 return ENOENT;
737
738 errno_t rc = ipc_phone_hangup(kobj->phone);
739 kobject_put(kobj);
740 cap_free(TASK, handle);
741 return rc;
742}
743
744/** Wait for an incoming IPC call or an answer.
745 *
746 * @param calldata Pointer to buffer where the call/answer data is stored.
747 * @param usec Timeout. See waitq_sleep_timeout() for explanation.
748 * @param flags Select mode of sleep operation. See waitq_sleep_timeout()
749 * for explanation.
750 *
751 * @return An error code on error.
752 */
753sys_errno_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec,
754 unsigned int flags)
755{
756 call_t *call = NULL;
757
758restart:
759
760#ifdef CONFIG_UDEBUG
761 udebug_stoppable_begin();
762#endif
763
764 errno_t rc = ipc_wait_for_call(&TASK->answerbox, usec,
765 flags | SYNCH_FLAGS_INTERRUPTIBLE, &call);
766
767#ifdef CONFIG_UDEBUG
768 udebug_stoppable_end();
769#endif
770
771 if (rc != EOK)
772 return rc;
773
774 assert(call);
775
776 call->data.flags = call->flags;
777 if (call->flags & IPC_CALL_NOTIF) {
778 /* Set the request_label to the interrupt counter */
779 call->data.request_label = (sysarg_t) call->priv;
780
781 call->data.cap_handle = CAP_NIL;
782
783 STRUCT_TO_USPACE(calldata, &call->data);
784 kobject_put(call->kobject);
785
786 return EOK;
787 }
788
789 if (call->flags & IPC_CALL_ANSWERED) {
790 process_answer(call);
791
792 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
793 kobject_put(call->kobject);
794 goto restart;
795 }
796
797 call->data.cap_handle = CAP_NIL;
798
799 STRUCT_TO_USPACE(calldata, &call->data);
800 kobject_put(call->kobject);
801
802 return EOK;
803 }
804
805 if (process_request(&TASK->answerbox, call))
806 goto restart;
807
808 cap_handle_t handle = CAP_NIL;
809 rc = cap_alloc(TASK, &handle);
810 if (rc != EOK) {
811 goto error;
812 }
813
814 call->data.cap_handle = handle;
815
816 /*
817 * Include phone hash of the caller in the request, copy the whole
818 * call->data, not only call->data.args.
819 */
820 rc = STRUCT_TO_USPACE(calldata, &call->data);
821 if (rc != EOK)
822 goto error;
823
824 kobject_add_ref(call->kobject);
825 cap_publish(TASK, handle, call->kobject);
826 return EOK;
827
828error:
829 if (CAP_HANDLE_VALID(handle))
830 cap_free(TASK, handle);
831
832 /*
833 * The callee will not receive this call and no one else has a chance to
834 * answer it. Set the IPC_CALL_AUTO_REPLY flag and return the EPARTY
835 * error code.
836 */
837 ipc_data_t saved_data;
838 bool saved;
839
840 if (answer_need_old(call)) {
841 memcpy(&saved_data, &call->data, sizeof(call->data));
842 saved = true;
843 } else
844 saved = false;
845
846 IPC_SET_RETVAL(call->data, EPARTY);
847 (void) answer_preprocess(call, saved ? &saved_data : NULL);
848 call->flags |= IPC_CALL_AUTO_REPLY;
849 ipc_answer(&TASK->answerbox, call);
850
851 return rc;
852}
853
854/** Interrupt one thread from sys_ipc_wait_for_call().
855 *
856 */
857sys_errno_t sys_ipc_poke(void)
858{
859 waitq_wakeup(&TASK->answerbox.wq, WAKEUP_FIRST);
860 return EOK;
861}
862
863/** Connect an IRQ handler to a task.
864 *
865 * @param inr IRQ number.
866 * @param imethod Interface and method to be associated with the notification.
867 * @param ucode Uspace pointer to the top-half pseudocode.
868 *
869 * @param[out] uspace_handle Uspace pointer to IRQ capability handle
870 *
871 * @return EPERM
872 * @return Error code returned by ipc_irq_subscribe().
873 *
874 */
875sys_errno_t sys_ipc_irq_subscribe(inr_t inr, sysarg_t imethod,
876 irq_code_t *ucode, cap_irq_handle_t *uspace_handle)
877{
878 if (!(perm_get(TASK) & PERM_IRQ_REG))
879 return EPERM;
880
881 return ipc_irq_subscribe(&TASK->answerbox, inr, imethod, ucode, uspace_handle);
882}
883
884/** Disconnect an IRQ handler from a task.
885 *
886 * @param handle IRQ capability handle.
887 *
888 * @return Zero on success or EPERM on error.
889 *
890 */
891sys_errno_t sys_ipc_irq_unsubscribe(cap_irq_handle_t handle)
892{
893 if (!(perm_get(TASK) & PERM_IRQ_REG))
894 return EPERM;
895
896 ipc_irq_unsubscribe(&TASK->answerbox, handle);
897
898 return 0;
899}
900
901/** Syscall connect to a task by ID
902 *
903 * @return Error code.
904 *
905 */
906sys_errno_t sys_ipc_connect_kbox(task_id_t *uspace_taskid,
907 cap_phone_handle_t *uspace_phone)
908{
909#ifdef CONFIG_UDEBUG
910 task_id_t taskid;
911 cap_phone_handle_t phone;
912
913 errno_t rc = copy_from_uspace(&taskid, uspace_taskid, sizeof(task_id_t));
914 if (rc == EOK) {
915 rc = ipc_connect_kbox((task_id_t) taskid, &phone);
916 }
917
918 if (rc == EOK) {
919 rc = copy_to_uspace(uspace_phone, &phone, sizeof(cap_handle_t));
920 if (rc != EOK) {
921 // Clean up the phone on failure.
922 sys_ipc_hangup(phone);
923 }
924 }
925
926 return (sys_errno_t) rc;
927#else
928 return (sys_errno_t) ENOTSUP;
929#endif
930}
931
932/** @}
933 */
Note: See TracBrowser for help on using the repository browser.