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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9314ee1 was 11675207, checked in by jermar <jermar@…>, 17 years ago

Move everything to kernel/.

  • Property mode set to 100644
File size: 14.9 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 <ipc/sysipc.h>
43#include <ipc/irq.h>
44#include <ipc/ipcrsc.h>
45#include <arch/interrupt.h>
46#include <print.h>
47#include <syscall/copy.h>
48#include <security/cap.h>
49#include <mm/as.h>
50
51#define GET_CHECK_PHONE(phone,phoneid,err) { \
52 if (phoneid > IPC_MAX_PHONES) { err; } \
53 phone = &TASK->phones[phoneid]; \
54}
55
56#define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*(src)))
57
58/** Return true if the method is a system method */
59static inline int is_system_method(unative_t method)
60{
61 if (method <= IPC_M_LAST_SYSTEM)
62 return 1;
63 return 0;
64}
65
66/** Return true if the message with this method is forwardable
67 *
68 * - some system messages may be forwarded, for some of them
69 * it is useless
70 */
71static inline int is_forwardable(unative_t method)
72{
73 if (method == IPC_M_PHONE_HUNGUP || method == IPC_M_AS_AREA_SEND \
74 || method == IPC_M_AS_AREA_RECV)
75 return 0; /* This message is meant only for the receiver */
76 return 1;
77}
78
79/****************************************************/
80/* Functions that preprocess answer before sending
81 * it to the recepient
82 */
83
84/** Return true if the caller (ipc_answer) should save
85 * the old call contents for answer_preprocess
86 */
87static inline int answer_need_old(call_t *call)
88{
89 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
90 return 1;
91 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
92 return 1;
93 if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_SEND)
94 return 1;
95 if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_RECV)
96 return 1;
97 return 0;
98}
99
100/** Interpret process answer as control information
101 *
102 * This function is called directly after sys_ipc_answer
103 */
104static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
105{
106 int phoneid;
107
108 if (IPC_GET_RETVAL(answer->data) == EHANGUP) {
109 /* In case of forward, hangup the forwared phone,
110 * not the originator
111 */
112 spinlock_lock(&answer->data.phone->lock);
113 spinlock_lock(&TASK->answerbox.lock);
114 if (answer->data.phone->state == IPC_PHONE_CONNECTED) {
115 list_remove(&answer->data.phone->link);
116 answer->data.phone->state = IPC_PHONE_SLAMMED;
117 }
118 spinlock_unlock(&TASK->answerbox.lock);
119 spinlock_unlock(&answer->data.phone->lock);
120 }
121
122 if (!olddata)
123 return 0;
124
125 if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
126 phoneid = IPC_GET_ARG3(*olddata);
127 if (IPC_GET_RETVAL(answer->data)) {
128 /* The connection was not accepted */
129 phone_dealloc(phoneid);
130 } else {
131 /* The connection was accepted */
132 phone_connect(phoneid,&answer->sender->answerbox);
133 /* Set 'phone identification' as arg3 of response */
134 IPC_SET_ARG3(answer->data, (unative_t)&TASK->phones[phoneid]);
135 }
136 } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
137 /* If the users accepted call, connect */
138 if (!IPC_GET_RETVAL(answer->data)) {
139 ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
140 &TASK->answerbox);
141 }
142 } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_SEND) {
143 if (!IPC_GET_RETVAL(answer->data)) { /* Accepted, handle as_area receipt */
144 ipl_t ipl;
145 int rc;
146 as_t *as;
147
148 ipl = interrupts_disable();
149 spinlock_lock(&answer->sender->lock);
150 as = answer->sender->as;
151 spinlock_unlock(&answer->sender->lock);
152 interrupts_restore(ipl);
153
154 rc = as_area_share(as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(*olddata),
155 AS, IPC_GET_ARG1(answer->data), IPC_GET_ARG3(*olddata));
156 IPC_SET_RETVAL(answer->data, rc);
157 return rc;
158 }
159 } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_RECV) {
160 if (!IPC_GET_RETVAL(answer->data)) {
161 ipl_t ipl;
162 as_t *as;
163 int rc;
164
165 ipl = interrupts_disable();
166 spinlock_lock(&answer->sender->lock);
167 as = answer->sender->as;
168 spinlock_unlock(&answer->sender->lock);
169 interrupts_restore(ipl);
170
171 rc = as_area_share(AS, IPC_GET_ARG1(answer->data), IPC_GET_ARG2(*olddata),
172 as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(answer->data));
173 IPC_SET_RETVAL(answer->data, rc);
174 }
175 }
176 return 0;
177}
178
179/** Called before the request is sent
180 *
181 * @return 0 - no error, -1 - report error to user
182 */
183static int request_preprocess(call_t *call)
184{
185 int newphid;
186 size_t size;
187
188 switch (IPC_GET_METHOD(call->data)) {
189 case IPC_M_CONNECT_ME_TO:
190 newphid = phone_alloc();
191 if (newphid < 0)
192 return ELIMIT;
193 /* Set arg3 for server */
194 IPC_SET_ARG3(call->data, (unative_t)&TASK->phones[newphid]);
195 call->flags |= IPC_CALL_CONN_ME_TO;
196 call->private = newphid;
197 break;
198 case IPC_M_AS_AREA_SEND:
199 size = as_get_size(IPC_GET_ARG1(call->data));
200 if (!size) {
201 return EPERM;
202 }
203 IPC_SET_ARG2(call->data, size);
204 break;
205 default:
206 break;
207 }
208 return 0;
209}
210
211/****************************************************/
212/* Functions called to process received call/answer
213 * before passing to uspace
214 */
215
216/** Do basic kernel processing of received call answer */
217static void process_answer(call_t *call)
218{
219 if (IPC_GET_RETVAL(call->data) == EHANGUP && \
220 call->flags & IPC_CALL_FORWARDED)
221 IPC_SET_RETVAL(call->data, EFORWARD);
222
223 if (call->flags & IPC_CALL_CONN_ME_TO) {
224 if (IPC_GET_RETVAL(call->data))
225 phone_dealloc(call->private);
226 else
227 IPC_SET_ARG3(call->data, call->private);
228 }
229}
230
231/** Do basic kernel processing of received call request
232 *
233 * @return 0 - the call should be passed to userspace, 1 - ignore call
234 */
235static int process_request(answerbox_t *box,call_t *call)
236{
237 int phoneid;
238
239 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
240 phoneid = phone_alloc();
241 if (phoneid < 0) { /* Failed to allocate phone */
242 IPC_SET_RETVAL(call->data, ELIMIT);
243 ipc_answer(box,call);
244 return -1;
245 }
246 IPC_SET_ARG3(call->data, phoneid);
247 }
248 return 0;
249}
250
251/** Send a call over IPC, wait for reply, return to user
252 *
253 * @return Call identification, returns -1 on fatal error,
254 -2 on 'Too many async request, handle answers first
255 */
256unative_t sys_ipc_call_sync_fast(unative_t phoneid, unative_t method,
257 unative_t arg1, ipc_data_t *data)
258{
259 call_t call;
260 phone_t *phone;
261 int res;
262
263 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
264
265 ipc_call_static_init(&call);
266 IPC_SET_METHOD(call.data, method);
267 IPC_SET_ARG1(call.data, arg1);
268
269 if (!(res=request_preprocess(&call))) {
270 ipc_call_sync(phone, &call);
271 process_answer(&call);
272 } else
273 IPC_SET_RETVAL(call.data, res);
274 STRUCT_TO_USPACE(&data->args, &call.data.args);
275
276 return 0;
277}
278
279/** Synchronous IPC call allowing to send whole message */
280unative_t sys_ipc_call_sync(unative_t phoneid, ipc_data_t *question,
281 ipc_data_t *reply)
282{
283 call_t call;
284 phone_t *phone;
285 int res;
286 int rc;
287
288 ipc_call_static_init(&call);
289 rc = copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
290 if (rc != 0)
291 return (unative_t) rc;
292
293 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
294
295 if (!(res=request_preprocess(&call))) {
296 ipc_call_sync(phone, &call);
297 process_answer(&call);
298 } else
299 IPC_SET_RETVAL(call.data, res);
300
301 rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
302 if (rc != 0)
303 return rc;
304
305 return 0;
306}
307
308/** Check that the task did not exceed allowed limit
309 *
310 * @return 0 - Limit OK, -1 - limit exceeded
311 */
312static int check_call_limit(void)
313{
314 if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
315 atomic_dec(&TASK->active_calls);
316 return -1;
317 }
318 return 0;
319}
320
321/** Send an asynchronous call over ipc
322 *
323 * @return Call identification, returns -1 on fatal error,
324 -2 on 'Too many async request, handle answers first
325 */
326unative_t sys_ipc_call_async_fast(unative_t phoneid, unative_t method,
327 unative_t arg1, unative_t arg2)
328{
329 call_t *call;
330 phone_t *phone;
331 int res;
332
333 if (check_call_limit())
334 return IPC_CALLRET_TEMPORARY;
335
336 GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
337
338 call = ipc_call_alloc(0);
339 IPC_SET_METHOD(call->data, method);
340 IPC_SET_ARG1(call->data, arg1);
341 IPC_SET_ARG2(call->data, arg2);
342 IPC_SET_ARG3(call->data, 0);
343
344 if (!(res=request_preprocess(call)))
345 ipc_call(phone, call);
346 else
347 ipc_backsend_err(phone, call, res);
348
349 return (unative_t) call;
350}
351
352/** Synchronous IPC call allowing to send whole message
353 *
354 * @return The same as sys_ipc_call_async
355 */
356unative_t sys_ipc_call_async(unative_t phoneid, ipc_data_t *data)
357{
358 call_t *call;
359 phone_t *phone;
360 int res;
361 int rc;
362
363 if (check_call_limit())
364 return IPC_CALLRET_TEMPORARY;
365
366 GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
367
368 call = ipc_call_alloc(0);
369 rc = copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
370 if (rc != 0) {
371 ipc_call_free(call);
372 return (unative_t) rc;
373 }
374 if (!(res=request_preprocess(call)))
375 ipc_call(phone, call);
376 else
377 ipc_backsend_err(phone, call, res);
378
379 return (unative_t) call;
380}
381
382/** Forward received call to another destination
383 *
384 * The arg1 and arg2 are changed in the forwarded message
385 *
386 * Warning: If implementing non-fast version, make sure that
387 * arg3 is not rewritten for certain system IPC
388 */
389unative_t sys_ipc_forward_fast(unative_t callid, unative_t phoneid,
390 unative_t method, unative_t arg1)
391{
392 call_t *call;
393 phone_t *phone;
394
395 call = get_call(callid);
396 if (!call)
397 return ENOENT;
398
399 call->flags |= IPC_CALL_FORWARDED;
400
401 GET_CHECK_PHONE(phone, phoneid, {
402 IPC_SET_RETVAL(call->data, EFORWARD);
403 ipc_answer(&TASK->answerbox, call);
404 return ENOENT;
405 });
406
407 if (!is_forwardable(IPC_GET_METHOD(call->data))) {
408 IPC_SET_RETVAL(call->data, EFORWARD);
409 ipc_answer(&TASK->answerbox, call);
410 return EPERM;
411 }
412
413 /* Userspace is not allowed to change method of system methods
414 * on forward, allow changing ARG1 and ARG2 by means of method and arg1
415 */
416 if (is_system_method(IPC_GET_METHOD(call->data))) {
417 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
418 phone_dealloc(IPC_GET_ARG3(call->data));
419
420 IPC_SET_ARG1(call->data, method);
421 IPC_SET_ARG2(call->data, arg1);
422 } else {
423 IPC_SET_METHOD(call->data, method);
424 IPC_SET_ARG1(call->data, arg1);
425 }
426
427 return ipc_forward(call, phone, &TASK->answerbox);
428}
429
430/** Send IPC answer */
431unative_t sys_ipc_answer_fast(unative_t callid, unative_t retval,
432 unative_t arg1, unative_t arg2)
433{
434 call_t *call;
435 ipc_data_t saved_data;
436 int saveddata = 0;
437 int rc;
438
439 /* Do not answer notification callids */
440 if (callid & IPC_CALLID_NOTIFICATION)
441 return 0;
442
443 call = get_call(callid);
444 if (!call)
445 return ENOENT;
446
447 if (answer_need_old(call)) {
448 memcpy(&saved_data, &call->data, sizeof(call->data));
449 saveddata = 1;
450 }
451
452 IPC_SET_RETVAL(call->data, retval);
453 IPC_SET_ARG1(call->data, arg1);
454 IPC_SET_ARG2(call->data, arg2);
455 rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
456
457 ipc_answer(&TASK->answerbox, call);
458 return rc;
459}
460
461/** Send IPC answer */
462unative_t sys_ipc_answer(unative_t callid, ipc_data_t *data)
463{
464 call_t *call;
465 ipc_data_t saved_data;
466 int saveddata = 0;
467 int rc;
468
469 /* Do not answer notification callids */
470 if (callid & IPC_CALLID_NOTIFICATION)
471 return 0;
472
473 call = get_call(callid);
474 if (!call)
475 return ENOENT;
476
477 if (answer_need_old(call)) {
478 memcpy(&saved_data, &call->data, sizeof(call->data));
479 saveddata = 1;
480 }
481 rc = copy_from_uspace(&call->data.args, &data->args,
482 sizeof(call->data.args));
483 if (rc != 0)
484 return rc;
485
486 rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
487
488 ipc_answer(&TASK->answerbox, call);
489
490 return rc;
491}
492
493/** Hang up the phone
494 *
495 */
496unative_t sys_ipc_hangup(int phoneid)
497{
498 phone_t *phone;
499
500 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
501
502 if (ipc_phone_hangup(phone))
503 return -1;
504
505 return 0;
506}
507
508/** Wait for incoming ipc call or answer
509 *
510 * @param calldata Pointer to buffer where the call/answer data is stored
511 * @param usec Timeout. See waitq_sleep_timeout() for explanation.
512 * @param flags Select mode of sleep operation. See waitq_sleep_timeout() for explanation.
513 *
514 * @return Callid, if callid & 1, then the call is answer
515 */
516unative_t sys_ipc_wait_for_call(ipc_data_t *calldata, uint32_t usec, int flags)
517{
518 call_t *call;
519
520restart:
521 call = ipc_wait_for_call(&TASK->answerbox, usec, flags | SYNCH_FLAGS_INTERRUPTIBLE);
522 if (!call)
523 return 0;
524
525 if (call->flags & IPC_CALL_NOTIF) {
526 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
527
528 /* Set in_phone_hash to the interrupt counter */
529 call->data.phone = (void *)call->private;
530
531 STRUCT_TO_USPACE(calldata, &call->data);
532
533 ipc_call_free(call);
534
535 return ((unative_t)call) | IPC_CALLID_NOTIFICATION;
536 }
537
538 if (call->flags & IPC_CALL_ANSWERED) {
539 process_answer(call);
540
541 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
542
543 atomic_dec(&TASK->active_calls);
544
545 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
546 ipc_call_free(call);
547 goto restart;
548 }
549
550 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
551 ipc_call_free(call);
552
553 return ((unative_t)call) | IPC_CALLID_ANSWERED;
554 }
555
556 if (process_request(&TASK->answerbox, call))
557 goto restart;
558
559 /* Include phone address('id') of the caller in the request,
560 * copy whole call->data, not only call->data.args */
561 if (STRUCT_TO_USPACE(calldata, &call->data)) {
562 return 0;
563 }
564 return (unative_t)call;
565}
566
567/** Connect irq handler to task */
568unative_t sys_ipc_register_irq(int irq, irq_code_t *ucode)
569{
570 if (!(cap_get(TASK) & CAP_IRQ_REG))
571 return EPERM;
572
573 if (irq >= IRQ_COUNT || irq <= -IPC_IRQ_RESERVED_VIRTUAL)
574 return (unative_t) ELIMIT;
575
576 irq_ipc_bind_arch(irq);
577
578 return ipc_irq_register(&TASK->answerbox, irq, ucode);
579}
580
581/* Disconnect irq handler from task */
582unative_t sys_ipc_unregister_irq(int irq)
583{
584 if (!(cap_get(TASK) & CAP_IRQ_REG))
585 return EPERM;
586
587 if (irq >= IRQ_COUNT || irq <= -IPC_IRQ_RESERVED_VIRTUAL)
588 return (unative_t) ELIMIT;
589
590 ipc_irq_unregister(&TASK->answerbox, irq);
591
592 return 0;
593}
594
595/** @}
596 */
Note: See TracBrowser for help on using the repository browser.