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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 314f4b59 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • 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 genericipc
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/***********************************************************************
122 * Functions that preprocess answer before sending it to the recepient.
123 ***********************************************************************/
124
125/** Decide if the caller (e.g. ipc_answer()) should save the old call contents
126 * for answer_preprocess().
127 *
128 * @param call Call structure to be decided.
129 *
130 * @return true if the old call contents should be saved.
131 *
132 */
133static inline bool answer_need_old(call_t *call)
134{
135 switch (IPC_GET_IMETHOD(call->data)) {
136 case IPC_M_CONNECT_TO_ME:
137 case IPC_M_CONNECT_ME_TO:
138 case IPC_M_PAGE_IN:
139 case IPC_M_SHARE_OUT:
140 case IPC_M_SHARE_IN:
141 case IPC_M_DATA_WRITE:
142 case IPC_M_DATA_READ:
143 case IPC_M_STATE_CHANGE_AUTHORIZE:
144 return true;
145 default:
146 return false;
147 }
148}
149
150/** Interpret process answer as control information.
151 *
152 * This function is called directly after sys_ipc_answer().
153 *
154 * @param answer Call structure with the answer.
155 * @param olddata Saved data of the request.
156 *
157 * @return Return EOK on success or an error code.
158 *
159 */
160errno_t answer_preprocess(call_t *answer, ipc_data_t *olddata)
161{
162 errno_t rc = EOK;
163
164 spinlock_lock(&answer->forget_lock);
165 if (answer->forget) {
166 /*
167 * This is a forgotten call and answer->sender is not valid.
168 */
169 spinlock_unlock(&answer->forget_lock);
170
171 SYSIPC_OP(answer_cleanup, answer, olddata);
172 return rc;
173 } else {
174 assert(answer->active);
175
176 /*
177 * Mark the call as inactive to prevent _ipc_answer_free_call()
178 * from attempting to remove the call from the active list
179 * itself.
180 */
181 answer->active = false;
182
183 /*
184 * Remove the call from the sender's active call list.
185 * We enforce this locking order so that any potential
186 * concurrently executing forget operation is forced to
187 * release its active_calls_lock and lose the race to
188 * forget this soon to be answered call.
189 */
190 spinlock_lock(&answer->sender->active_calls_lock);
191 list_remove(&answer->ta_link);
192 spinlock_unlock(&answer->sender->active_calls_lock);
193 }
194 spinlock_unlock(&answer->forget_lock);
195
196 if ((errno_t) IPC_GET_RETVAL(answer->data) == EHANGUP) {
197 phone_t *phone = answer->caller_phone;
198 mutex_lock(&phone->lock);
199 if (phone->state == IPC_PHONE_CONNECTED) {
200 irq_spinlock_lock(&phone->callee->lock, true);
201 list_remove(&phone->link);
202 /* Drop callee->connected_phones reference */
203 kobject_put(phone->kobject);
204 phone->state = IPC_PHONE_SLAMMED;
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
249/** Do basic kernel processing of received call request.
250 *
251 * @param box Destination answerbox structure.
252 * @param call Call structure with the request.
253 *
254 * @return 0 if the call should be passed to userspace.
255 * @return -1 if the call should be ignored.
256 *
257 */
258static int process_request(answerbox_t *box, call_t *call)
259{
260 return SYSIPC_OP(request_process, call, box);
261}
262
263/** Make a call over IPC and wait for reply.
264 *
265 * @param handle Phone capability handle for the call.
266 * @param data[inout] Structure with request/reply data.
267 * @param priv Value to be stored in call->priv.
268 *
269 * @return EOK on success.
270 * @return ENOENT if there is no such phone handle.
271 *
272 */
273errno_t ipc_req_internal(cap_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_get(&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(sysarg_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.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(sysarg_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.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(sysarg_t chandle, sysarg_t phandle,
469 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
470 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) == IPC_M_CONNECT_TO_ME)
508 phone_dealloc(IPC_GET_ARG5(call->data));
509
510 IPC_SET_ARG1(call->data, imethod);
511 IPC_SET_ARG2(call->data, arg1);
512 IPC_SET_ARG3(call->data, arg2);
513
514 if (slow)
515 IPC_SET_ARG4(call->data, arg3);
516
517 /*
518 * For system methods we deliberately don't
519 * overwrite ARG5.
520 */
521 } else {
522 IPC_SET_IMETHOD(call->data, imethod);
523 IPC_SET_ARG1(call->data, arg1);
524 IPC_SET_ARG2(call->data, arg2);
525 if (slow) {
526 IPC_SET_ARG3(call->data, arg3);
527 IPC_SET_ARG4(call->data, arg4);
528 IPC_SET_ARG5(call->data, arg5);
529 }
530 }
531 }
532
533 rc = ipc_forward(call, pkobj->phone, &TASK->answerbox, mode);
534 if (rc != EOK) {
535 after_forward = true;
536 goto error;
537 }
538
539 cap_free(TASK, chandle);
540 kobject_put(ckobj);
541 kobject_put(pkobj);
542 return EOK;
543
544error:
545 IPC_SET_RETVAL(call->data, EFORWARD);
546 (void) answer_preprocess(call, need_old ? &old : NULL);
547 if (after_forward)
548 _ipc_answer_free_call(call, false);
549 else
550 ipc_answer(&TASK->answerbox, call);
551
552 /* Republish the capability so that the call does not get lost. */
553 cap_publish(TASK, chandle, ckobj);
554
555 if (pkobj)
556 kobject_put(pkobj);
557 return rc;
558}
559
560/** Forward a received call to another destination - fast version.
561 *
562 * In case the original interface and method is a system method, ARG1, ARG2
563 * and ARG3 are overwritten in the forwarded message with the new method and
564 * the new arg1 and arg2, respectively. Otherwise the IMETHOD, ARG1 and ARG2
565 * are rewritten with the new interface and method, arg1 and arg2, respectively.
566 * Also note there is a set of immutable methods, for which the new method and
567 * arguments are not set and these values are ignored.
568 *
569 * @param chandle Call handle of the call to forward.
570 * @param phandle Phone handle to use for forwarding.
571 * @param imethod New interface and method to use for the forwarded call.
572 * @param arg1 New value of the first argument for the forwarded call.
573 * @param arg2 New value of the second argument for the forwarded call.
574 * @param mode Flags that specify mode of the forward operation.
575 *
576 * @return 0 on succes, otherwise an error code.
577 *
578 */
579sys_errno_t sys_ipc_forward_fast(sysarg_t chandle, sysarg_t phandle,
580 sysarg_t imethod, sysarg_t arg1, sysarg_t arg2, unsigned int mode)
581{
582 return sys_ipc_forward_common(chandle, phandle, imethod, arg1, arg2, 0,
583 0, 0, mode, false);
584}
585
586/** Forward a received call to another destination - slow version.
587 *
588 * This function is the slow verision of the sys_ipc_forward_fast interface.
589 * It can copy all five new arguments and the new interface and method from
590 * the userspace. It naturally extends the functionality of the fast version.
591 * For system methods, it additionally stores the new value of arg3 to ARG4.
592 * For non-system methods, it additionally stores the new value of arg3, arg4
593 * and arg5, respectively, to ARG3, ARG4 and ARG5, respectively.
594 *
595 * @param chandle Call handle of the call to forward.
596 * @param phandle Phone handle to use for forwarding.
597 * @param data Userspace address of the new IPC data.
598 * @param mode Flags that specify mode of the forward operation.
599 *
600 * @return 0 on succes, otherwise an error code.
601 *
602 */
603sys_errno_t sys_ipc_forward_slow(sysarg_t chandle, sysarg_t phandle,
604 ipc_data_t *data, unsigned int mode)
605{
606 ipc_data_t newdata;
607 errno_t rc = copy_from_uspace(&newdata.args, &data->args,
608 sizeof(newdata.args));
609 if (rc != EOK)
610 return (sys_errno_t) rc;
611
612 return sys_ipc_forward_common(chandle, phandle,
613 IPC_GET_IMETHOD(newdata), IPC_GET_ARG1(newdata),
614 IPC_GET_ARG2(newdata), IPC_GET_ARG3(newdata),
615 IPC_GET_ARG4(newdata), IPC_GET_ARG5(newdata), mode, true);
616}
617
618/** Answer an IPC call - fast version.
619 *
620 * This function can handle only two return arguments of payload, but is faster
621 * than the generic sys_ipc_answer().
622 *
623 * @param chandle Call handle to be answered.
624 * @param retval Return value of the answer.
625 * @param arg1 Service-defined return value.
626 * @param arg2 Service-defined return value.
627 * @param arg3 Service-defined return value.
628 * @param arg4 Service-defined return value.
629 *
630 * @return 0 on success, otherwise an error code.
631 *
632 */
633sys_errno_t sys_ipc_answer_fast(sysarg_t chandle, sysarg_t retval, sysarg_t arg1,
634 sysarg_t arg2, sysarg_t arg3, sysarg_t arg4)
635{
636 kobject_t *kobj = cap_unpublish(TASK, chandle, KOBJECT_TYPE_CALL);
637 if (!kobj)
638 return ENOENT;
639
640 call_t *call = kobj->call;
641
642 ipc_data_t saved_data;
643 bool saved;
644
645 if (answer_need_old(call)) {
646 memcpy(&saved_data, &call->data, sizeof(call->data));
647 saved = true;
648 } else
649 saved = false;
650
651 IPC_SET_RETVAL(call->data, retval);
652 IPC_SET_ARG1(call->data, arg1);
653 IPC_SET_ARG2(call->data, arg2);
654 IPC_SET_ARG3(call->data, arg3);
655 IPC_SET_ARG4(call->data, arg4);
656
657 /*
658 * To achieve deterministic behavior, zero out arguments that are beyond
659 * the limits of the fast version.
660 */
661 IPC_SET_ARG5(call->data, 0);
662 errno_t rc = answer_preprocess(call, saved ? &saved_data : NULL);
663
664 ipc_answer(&TASK->answerbox, call);
665
666 kobject_put(kobj);
667 cap_free(TASK, chandle);
668
669 return rc;
670}
671
672/** Answer an IPC call.
673 *
674 * @param chandle Call handle to be answered.
675 * @param data Userspace address of call data with the answer.
676 *
677 * @return 0 on success, otherwise an error code.
678 *
679 */
680sys_errno_t sys_ipc_answer_slow(sysarg_t chandle, ipc_data_t *data)
681{
682 kobject_t *kobj = cap_unpublish(TASK, chandle, KOBJECT_TYPE_CALL);
683 if (!kobj)
684 return ENOENT;
685
686 call_t *call = kobj->call;
687
688 ipc_data_t saved_data;
689 bool saved;
690
691 if (answer_need_old(call)) {
692 memcpy(&saved_data, &call->data, sizeof(call->data));
693 saved = true;
694 } else
695 saved = false;
696
697 errno_t rc = copy_from_uspace(&call->data.args, &data->args,
698 sizeof(call->data.args));
699 if (rc != EOK) {
700 /*
701 * Republish the capability so that the call does not get lost.
702 */
703 cap_publish(TASK, chandle, kobj);
704 return rc;
705 }
706
707 rc = answer_preprocess(call, saved ? &saved_data : NULL);
708
709 ipc_answer(&TASK->answerbox, call);
710
711 kobject_put(kobj);
712 cap_free(TASK, chandle);
713
714 return rc;
715}
716
717/** Hang up a phone.
718 *
719 * @param handle Phone capability handle of the phone to be hung up.
720 *
721 * @return 0 on success or an error code.
722 *
723 */
724sys_errno_t sys_ipc_hangup(sysarg_t handle)
725{
726 kobject_t *kobj = kobject_get(TASK, handle, KOBJECT_TYPE_PHONE);
727 if (!kobj)
728 return ENOENT;
729
730 errno_t rc = ipc_phone_hangup(kobj->phone);
731 kobject_put(kobj);
732 return rc;
733}
734
735/** Wait for an incoming IPC call or an answer.
736 *
737 * @param calldata Pointer to buffer where the call/answer data is stored.
738 * @param usec Timeout. See waitq_sleep_timeout() for explanation.
739 * @param flags Select mode of sleep operation. See waitq_sleep_timeout()
740 * for explanation.
741 *
742 * @return An error code on error.
743 */
744sys_errno_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec,
745 unsigned int flags)
746{
747 call_t *call;
748
749restart:
750
751#ifdef CONFIG_UDEBUG
752 udebug_stoppable_begin();
753#endif
754
755 call = ipc_wait_for_call(&TASK->answerbox, usec,
756 flags | SYNCH_FLAGS_INTERRUPTIBLE);
757
758#ifdef CONFIG_UDEBUG
759 udebug_stoppable_end();
760#endif
761
762 if (!call) {
763 ipc_data_t data = {};
764 data.cap_handle = CAP_NIL;
765 STRUCT_TO_USPACE(calldata, &data);
766 return EOK;
767 }
768
769 call->data.flags = call->flags;
770 if (call->flags & IPC_CALL_NOTIF) {
771 /* Set in_phone_hash to the interrupt counter */
772 call->data.phone = (void *) call->priv;
773
774 call->data.cap_handle = CAP_NIL;
775
776 STRUCT_TO_USPACE(calldata, &call->data);
777 kobject_put(call->kobject);
778
779 return EOK;
780 }
781
782 if (call->flags & IPC_CALL_ANSWERED) {
783 process_answer(call);
784
785 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
786 kobject_put(call->kobject);
787 goto restart;
788 }
789
790 call->data.cap_handle = CAP_NIL;
791
792 STRUCT_TO_USPACE(calldata, &call->data);
793 kobject_put(call->kobject);
794
795 return EOK;
796 }
797
798 if (process_request(&TASK->answerbox, call))
799 goto restart;
800
801 cap_handle_t handle;
802 errno_t rc = cap_alloc(TASK, &handle);
803 if (rc != EOK) {
804 goto error;
805 }
806
807 call->data.cap_handle = handle;
808
809 /*
810 * Include phone hash of the caller in the request, copy the whole
811 * call->data, not only call->data.args.
812 */
813 rc = STRUCT_TO_USPACE(calldata, &call->data);
814 if (rc != EOK)
815 goto error;
816
817 kobject_add_ref(call->kobject);
818 cap_publish(TASK, handle, call->kobject);
819 return EOK;
820
821error:
822 if (handle >= 0)
823 cap_free(TASK, handle);
824
825 /*
826 * The callee will not receive this call and no one else has a chance to
827 * answer it. Set the IPC_CALL_AUTO_REPLY flag and return the EPARTY
828 * error code.
829 */
830 ipc_data_t saved_data;
831 bool saved;
832
833 if (answer_need_old(call)) {
834 memcpy(&saved_data, &call->data, sizeof(call->data));
835 saved = true;
836 } else
837 saved = false;
838
839 IPC_SET_RETVAL(call->data, EPARTY);
840 (void) answer_preprocess(call, saved ? &saved_data : NULL);
841 call->flags |= IPC_CALL_AUTO_REPLY;
842 ipc_answer(&TASK->answerbox, call);
843
844 return rc;
845}
846
847/** Interrupt one thread from sys_ipc_wait_for_call().
848 *
849 */
850sys_errno_t sys_ipc_poke(void)
851{
852 waitq_unsleep(&TASK->answerbox.wq);
853 return EOK;
854}
855
856/** Connect an IRQ handler to a task.
857 *
858 * @param inr IRQ number.
859 * @param imethod Interface and method to be associated with the notification.
860 * @param ucode Uspace pointer to the top-half pseudocode.
861 *
862 * @param[out] uspace_handle Uspace pointer to IRQ kernel object capability
863 *
864 * @return EPERM
865 * @return Error code returned by ipc_irq_subscribe().
866 *
867 */
868sys_errno_t sys_ipc_irq_subscribe(inr_t inr, sysarg_t imethod, irq_code_t *ucode,
869 cap_handle_t *uspace_handle)
870{
871 if (!(perm_get(TASK) & PERM_IRQ_REG))
872 return EPERM;
873
874 return ipc_irq_subscribe(&TASK->answerbox, inr, imethod, ucode, uspace_handle);
875}
876
877/** Disconnect an IRQ handler from a task.
878 *
879 * @param inr IRQ number.
880 * @param devno Device number.
881 *
882 * @return Zero on success or EPERM on error.
883 *
884 */
885sys_errno_t sys_ipc_irq_unsubscribe(sysarg_t cap)
886{
887 if (!(perm_get(TASK) & PERM_IRQ_REG))
888 return EPERM;
889
890 ipc_irq_unsubscribe(&TASK->answerbox, cap);
891
892 return 0;
893}
894
895/** Syscall connect to a task by ID
896 *
897 * @return Error code.
898 *
899 */
900sys_errno_t sys_ipc_connect_kbox(task_id_t *uspace_taskid, cap_handle_t *uspace_phone)
901{
902#ifdef CONFIG_UDEBUG
903 task_id_t taskid;
904 cap_handle_t phone;
905
906 errno_t rc = copy_from_uspace(&taskid, uspace_taskid, sizeof(task_id_t));
907 if (rc == EOK) {
908 rc = ipc_connect_kbox((task_id_t) taskid, &phone);
909 }
910
911 if (rc == EOK) {
912 rc = copy_to_uspace(uspace_phone, &phone, sizeof(cap_handle_t));
913 if (rc != EOK) {
914 // Clean up the phone on failure.
915 sys_ipc_hangup(phone);
916 }
917 }
918
919 return (sys_errno_t) rc;
920#else
921 return (sys_errno_t) ENOTSUP;
922#endif
923}
924
925/** @}
926 */
Note: See TracBrowser for help on using the repository browser.