Modify as_area_share() to accept destination area flags bitmask from the sender
[helenos.git] / generic / src / ipc / sysipc.c
blob1ebedf19edc747686e35f290d59207f605f01dfc
1 /*
2 * Copyright (C) 2006 Ondrej Palkovsky
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
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.
29 #include <arch.h>
30 #include <proc/task.h>
31 #include <proc/thread.h>
32 #include <errno.h>
33 #include <memstr.h>
34 #include <debug.h>
35 #include <ipc/ipc.h>
36 #include <ipc/sysipc.h>
37 #include <ipc/irq.h>
38 #include <ipc/ipcrsc.h>
39 #include <arch/interrupt.h>
40 #include <print.h>
41 #include <syscall/copy.h>
42 #include <security/cap.h>
43 #include <mm/as.h>
45 #define GET_CHECK_PHONE(phone,phoneid,err) { \
46 if (phoneid > IPC_MAX_PHONES) { err; } \
47 phone = &TASK->phones[phoneid]; \
50 #define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*(src)))
52 /** Return true if the method is a system method */
53 static inline int is_system_method(__native method)
55 if (method <= IPC_M_LAST_SYSTEM)
56 return 1;
57 return 0;
60 /** Return true if the message with this method is forwardable
62 * - some system messages may be forwarded, for some of them
63 * it is useless
65 static inline int is_forwardable(__native method)
67 if (method == IPC_M_PHONE_HUNGUP || method == IPC_M_AS_AREA_SEND)
68 return 0; /* This message is meant only for the receiver */
69 return 1;
72 /****************************************************/
73 /* Functions that preprocess answer before sending
74 * it to the recepient
77 /** Return true if the caller (ipc_answer) should save
78 * the old call contents for answer_preprocess
80 static inline int answer_need_old(call_t *call)
82 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
83 return 1;
84 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
85 return 1;
86 if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_SEND)
87 return 1;
88 return 0;
91 /** Interpret process answer as control information */
92 static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
94 int phoneid;
96 if (IPC_GET_RETVAL(answer->data) == EHANGUP) {
97 /* In case of forward, hangup the forwared phone,
98 * not the originator
100 spinlock_lock(&answer->data.phone->lock);
101 spinlock_lock(&TASK->answerbox.lock);
102 if (answer->data.phone->callee) {
103 list_remove(&answer->data.phone->list);
104 answer->data.phone->callee = 0;
106 spinlock_unlock(&TASK->answerbox.lock);
107 spinlock_unlock(&answer->data.phone->lock);
110 if (!olddata)
111 return 0;
113 if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
114 phoneid = IPC_GET_ARG3(*olddata);
115 if (IPC_GET_RETVAL(answer->data)) {
116 /* The connection was not accepted */
117 phone_dealloc(phoneid);
118 } else {
119 /* The connection was accepted */
120 phone_connect(phoneid,&answer->sender->answerbox);
121 /* Set 'phone identification' as arg3 of response */
122 IPC_SET_ARG3(answer->data, (__native)&TASK->phones[phoneid]);
124 } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
125 /* If the users accepted call, connect */
126 if (!IPC_GET_RETVAL(answer->data)) {
127 ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
128 &TASK->answerbox);
130 } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_SEND) {
131 if (!IPC_GET_RETVAL(answer->data)) { /* Accepted, handle as_area receipt */
132 ipl_t ipl;
133 as_t *as;
135 ipl = interrupts_disable();
136 spinlock_lock(&answer->sender->lock);
137 as = answer->sender->as;
138 spinlock_unlock(&answer->sender->lock);
139 interrupts_restore(ipl);
141 return as_area_share(as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(*olddata),
142 IPC_GET_ARG1(answer->data), IPC_GET_ARG3(*olddata));
145 return 0;
148 /** Called before the request is sent
150 * @return 0 - no error, -1 - report error to user
152 static int request_preprocess(call_t *call)
154 int newphid;
155 size_t size;
157 switch (IPC_GET_METHOD(call->data)) {
158 case IPC_M_CONNECT_ME_TO:
159 newphid = phone_alloc();
160 if (newphid < 0)
161 return ELIMIT;
162 /* Set arg3 for server */
163 IPC_SET_ARG3(call->data, (__native)&TASK->phones[newphid]);
164 call->flags |= IPC_CALL_CONN_ME_TO;
165 call->private = newphid;
166 break;
167 case IPC_M_AS_AREA_SEND:
168 size = as_get_size(IPC_GET_ARG1(call->data));
169 if (!size) {
170 return EPERM;
172 IPC_SET_ARG2(call->data, size);
173 break;
174 default:
175 break;
177 return 0;
180 /****************************************************/
181 /* Functions called to process received call/answer
182 * before passing to uspace
185 /** Do basic kernel processing of received call answer */
186 static void process_answer(call_t *call)
188 if (IPC_GET_RETVAL(call->data) == EHANGUP && \
189 call->flags & IPC_CALL_FORWARDED)
190 IPC_SET_RETVAL(call->data, EFORWARD);
192 if (call->flags & IPC_CALL_CONN_ME_TO) {
193 if (IPC_GET_RETVAL(call->data))
194 phone_dealloc(call->private);
195 else
196 IPC_SET_ARG3(call->data, call->private);
200 /** Do basic kernel processing of received call request
202 * @return 0 - the call should be passed to userspace, 1 - ignore call
204 static int process_request(answerbox_t *box,call_t *call)
206 int phoneid;
208 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
209 phoneid = phone_alloc();
210 if (phoneid < 0) { /* Failed to allocate phone */
211 IPC_SET_RETVAL(call->data, ELIMIT);
212 ipc_answer(box,call);
213 return -1;
215 IPC_SET_ARG3(call->data, phoneid);
217 return 0;
220 /** Send a call over IPC, wait for reply, return to user
222 * @return Call identification, returns -1 on fatal error,
223 -2 on 'Too many async request, handle answers first
225 __native sys_ipc_call_sync_fast(__native phoneid, __native method,
226 __native arg1, ipc_data_t *data)
228 call_t call;
229 phone_t *phone;
230 int res;
232 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
234 ipc_call_static_init(&call);
235 IPC_SET_METHOD(call.data, method);
236 IPC_SET_ARG1(call.data, arg1);
238 if (!(res=request_preprocess(&call))) {
239 ipc_call_sync(phone, &call);
240 process_answer(&call);
241 } else
242 IPC_SET_RETVAL(call.data, res);
243 STRUCT_TO_USPACE(&data->args, &call.data.args);
245 return 0;
248 /** Synchronous IPC call allowing to send whole message */
249 __native sys_ipc_call_sync(__native phoneid, ipc_data_t *question,
250 ipc_data_t *reply)
252 call_t call;
253 phone_t *phone;
254 int res;
255 int rc;
257 ipc_call_static_init(&call);
258 rc = copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
259 if (rc != 0)
260 return (__native) rc;
262 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
264 if (!(res=request_preprocess(&call))) {
265 ipc_call_sync(phone, &call);
266 process_answer(&call);
267 } else
268 IPC_SET_RETVAL(call.data, res);
270 rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
271 if (rc != 0)
272 return rc;
274 return 0;
277 /** Check that the task did not exceed allowed limit
279 * @return 0 - Limit OK, -1 - limit exceeded
281 static int check_call_limit(void)
283 if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
284 atomic_dec(&TASK->active_calls);
285 return -1;
287 return 0;
290 /** Send an asynchronous call over ipc
292 * @return Call identification, returns -1 on fatal error,
293 -2 on 'Too many async request, handle answers first
295 __native sys_ipc_call_async_fast(__native phoneid, __native method,
296 __native arg1, __native arg2)
298 call_t *call;
299 phone_t *phone;
300 int res;
302 if (check_call_limit())
303 return IPC_CALLRET_TEMPORARY;
305 GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
307 call = ipc_call_alloc(0);
308 IPC_SET_METHOD(call->data, method);
309 IPC_SET_ARG1(call->data, arg1);
310 IPC_SET_ARG2(call->data, arg2);
312 if (!(res=request_preprocess(call)))
313 ipc_call(phone, call);
314 else
315 ipc_backsend_err(phone, call, res);
317 return (__native) call;
320 /** Synchronous IPC call allowing to send whole message
322 * @return The same as sys_ipc_call_async
324 __native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
326 call_t *call;
327 phone_t *phone;
328 int res;
329 int rc;
331 if (check_call_limit())
332 return IPC_CALLRET_TEMPORARY;
334 GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
336 call = ipc_call_alloc(0);
337 rc = copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
338 if (rc != 0) {
339 ipc_call_free(call);
340 return (__native) rc;
342 if (!(res=request_preprocess(call)))
343 ipc_call(phone, call);
344 else
345 ipc_backsend_err(phone, call, res);
347 return (__native) call;
350 /** Forward received call to another destination
352 * The arg1 and arg2 are changed in the forwarded message
354 * Warning: If implementing non-fast version, make sure that
355 * arg3 is not rewritten for certain system IPC
357 __native sys_ipc_forward_fast(__native callid, __native phoneid,
358 __native method, __native arg1)
360 call_t *call;
361 phone_t *phone;
363 call = get_call(callid);
364 if (!call)
365 return ENOENT;
367 call->flags |= IPC_CALL_FORWARDED;
369 GET_CHECK_PHONE(phone, phoneid, {
370 IPC_SET_RETVAL(call->data, EFORWARD);
371 ipc_answer(&TASK->answerbox, call);
372 return ENOENT;
373 });
375 if (!is_forwardable(IPC_GET_METHOD(call->data))) {
376 IPC_SET_RETVAL(call->data, EFORWARD);
377 ipc_answer(&TASK->answerbox, call);
378 return EPERM;
381 /* Userspace is not allowed to change method of system methods
382 * on forward, allow changing ARG1 and ARG2 by means of method and arg1
384 if (is_system_method(IPC_GET_METHOD(call->data))) {
385 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
386 phone_dealloc(IPC_GET_ARG3(call->data));
388 IPC_SET_ARG1(call->data, method);
389 IPC_SET_ARG2(call->data, arg1);
390 } else {
391 IPC_SET_METHOD(call->data, method);
392 IPC_SET_ARG1(call->data, arg1);
395 return ipc_forward(call, phone, &TASK->answerbox);
398 /** Send IPC answer */
399 __native sys_ipc_answer_fast(__native callid, __native retval,
400 __native arg1, __native arg2)
402 call_t *call;
403 ipc_data_t saved_data;
404 int saveddata = 0;
405 int rc;
407 /* Do not answer notification callids */
408 if (callid & IPC_CALLID_NOTIFICATION)
409 return 0;
411 call = get_call(callid);
412 if (!call)
413 return ENOENT;
415 if (answer_need_old(call)) {
416 memcpy(&saved_data, &call->data, sizeof(call->data));
417 saveddata = 1;
420 IPC_SET_RETVAL(call->data, retval);
421 IPC_SET_ARG1(call->data, arg1);
422 IPC_SET_ARG2(call->data, arg2);
423 rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
425 ipc_answer(&TASK->answerbox, call);
426 return rc;
429 /** Send IPC answer */
430 __native sys_ipc_answer(__native callid, ipc_data_t *data)
432 call_t *call;
433 ipc_data_t saved_data;
434 int saveddata = 0;
435 int rc;
437 /* Do not answer notification callids */
438 if (callid & IPC_CALLID_NOTIFICATION)
439 return 0;
441 call = get_call(callid);
442 if (!call)
443 return ENOENT;
445 if (answer_need_old(call)) {
446 memcpy(&saved_data, &call->data, sizeof(call->data));
447 saveddata = 1;
449 rc = copy_from_uspace(&call->data.args, &data->args,
450 sizeof(call->data.args));
451 if (rc != 0)
452 return rc;
454 rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
456 ipc_answer(&TASK->answerbox, call);
458 return rc;
461 /** Hang up the phone
464 __native sys_ipc_hangup(int phoneid)
466 phone_t *phone;
468 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
470 if (ipc_phone_hangup(phone))
471 return -1;
473 return 0;
476 /** Wait for incoming ipc call or answer
478 * @param calldata Pointer to buffer where the call/answer data is stored
479 * @param usec Timeout. See waitq_sleep_timeout() for explanation.
480 * @param nonblocking See waitq_sleep_timeout() for explanation.
482 * @return Callid, if callid & 1, then the call is answer
484 __native sys_ipc_wait_for_call(ipc_data_t *calldata, __u32 usec, int nonblocking)
486 call_t *call;
488 restart:
489 call = ipc_wait_for_call(&TASK->answerbox, usec, nonblocking);
490 if (!call)
491 return 0;
493 if (call->flags & IPC_CALL_NOTIF) {
494 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
495 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
496 ipc_call_free(call);
498 return ((__native)call) | IPC_CALLID_NOTIFICATION;
501 if (call->flags & IPC_CALL_ANSWERED) {
502 process_answer(call);
504 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
506 atomic_dec(&TASK->active_calls);
508 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
509 ipc_call_free(call);
510 goto restart;
513 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
514 ipc_call_free(call);
516 return ((__native)call) | IPC_CALLID_ANSWERED;
519 if (process_request(&TASK->answerbox, call))
520 goto restart;
522 /* Include phone address('id') of the caller in the request,
523 * copy whole call->data, not only call->data.args */
524 if (STRUCT_TO_USPACE(calldata, &call->data)) {
525 return 0;
527 return (__native)call;
530 /** Connect irq handler to task */
531 __native sys_ipc_register_irq(__native irq, irq_code_t *ucode)
533 if (!(cap_get(TASK) & CAP_IRQ_REG))
534 return EPERM;
536 if (irq >= IRQ_COUNT)
537 return (__native) ELIMIT;
539 irq_ipc_bind_arch(irq);
541 return ipc_irq_register(&TASK->answerbox, irq, ucode);
544 /* Disconnect irq handler from task */
545 __native sys_ipc_unregister_irq(__native irq)
547 if (!(cap_get(TASK) & CAP_IRQ_REG))
548 return EPERM;
550 if (irq >= IRQ_COUNT)
551 return (__native) ELIMIT;
553 ipc_irq_unregister(&TASK->answerbox, irq);
555 return 0;