source: mainline/generic/src/ipc/sysipc.c@ 8497711

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

Rename IPC_M_AS_SEND to IPC_M_AS_AREA_SEND.

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