00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00035 #include <arch.h>
00036 #include <proc/task.h>
00037 #include <proc/thread.h>
00038 #include <errno.h>
00039 #include <memstr.h>
00040 #include <debug.h>
00041 #include <ipc/ipc.h>
00042 #include <ipc/sysipc.h>
00043 #include <ipc/irq.h>
00044 #include <ipc/ipcrsc.h>
00045 #include <arch/interrupt.h>
00046 #include <print.h>
00047 #include <syscall/copy.h>
00048 #include <security/cap.h>
00049 #include <mm/as.h>
00050
00051 #define GET_CHECK_PHONE(phone,phoneid,err) { \
00052 if (phoneid > IPC_MAX_PHONES) { err; } \
00053 phone = &TASK->phones[phoneid]; \
00054 }
00055
00056 #define STRUCT_TO_USPACE(dst,src) copy_to_uspace(dst,src,sizeof(*(src)))
00057
00059 static inline int is_system_method(__native method)
00060 {
00061 if (method <= IPC_M_LAST_SYSTEM)
00062 return 1;
00063 return 0;
00064 }
00065
00071 static inline int is_forwardable(__native method)
00072 {
00073 if (method == IPC_M_PHONE_HUNGUP || method == IPC_M_AS_AREA_SEND \
00074 || method == IPC_M_AS_AREA_RECV)
00075 return 0;
00076 return 1;
00077 }
00078
00079
00080
00081
00082
00083
00087 static inline int answer_need_old(call_t *call)
00088 {
00089 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
00090 return 1;
00091 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
00092 return 1;
00093 if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_SEND)
00094 return 1;
00095 if (IPC_GET_METHOD(call->data) == IPC_M_AS_AREA_RECV)
00096 return 1;
00097 return 0;
00098 }
00099
00104 static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
00105 {
00106 int phoneid;
00107
00108 if (IPC_GET_RETVAL(answer->data) == EHANGUP) {
00109
00110
00111
00112 spinlock_lock(&answer->data.phone->lock);
00113 spinlock_lock(&TASK->answerbox.lock);
00114 if (answer->data.phone->state == IPC_PHONE_CONNECTED) {
00115 list_remove(&answer->data.phone->link);
00116 answer->data.phone->state = IPC_PHONE_SLAMMED;
00117 }
00118 spinlock_unlock(&TASK->answerbox.lock);
00119 spinlock_unlock(&answer->data.phone->lock);
00120 }
00121
00122 if (!olddata)
00123 return 0;
00124
00125 if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
00126 phoneid = IPC_GET_ARG3(*olddata);
00127 if (IPC_GET_RETVAL(answer->data)) {
00128
00129 phone_dealloc(phoneid);
00130 } else {
00131
00132 phone_connect(phoneid,&answer->sender->answerbox);
00133
00134 IPC_SET_ARG3(answer->data, (__native)&TASK->phones[phoneid]);
00135 }
00136 } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
00137
00138 if (!IPC_GET_RETVAL(answer->data)) {
00139 ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
00140 &TASK->answerbox);
00141 }
00142 } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_SEND) {
00143 if (!IPC_GET_RETVAL(answer->data)) {
00144 ipl_t ipl;
00145 int rc;
00146 as_t *as;
00147
00148 ipl = interrupts_disable();
00149 spinlock_lock(&answer->sender->lock);
00150 as = answer->sender->as;
00151 spinlock_unlock(&answer->sender->lock);
00152 interrupts_restore(ipl);
00153
00154 rc = as_area_share(as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(*olddata),
00155 AS, IPC_GET_ARG1(answer->data), IPC_GET_ARG3(*olddata));
00156 IPC_SET_RETVAL(answer->data, rc);
00157 return rc;
00158 }
00159 } else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_AREA_RECV) {
00160 if (!IPC_GET_RETVAL(answer->data)) {
00161 ipl_t ipl;
00162 as_t *as;
00163 int rc;
00164
00165 ipl = interrupts_disable();
00166 spinlock_lock(&answer->sender->lock);
00167 as = answer->sender->as;
00168 spinlock_unlock(&answer->sender->lock);
00169 interrupts_restore(ipl);
00170
00171 rc = as_area_share(AS, IPC_GET_ARG1(answer->data), IPC_GET_ARG2(*olddata),
00172 as, IPC_GET_ARG1(*olddata), IPC_GET_ARG2(answer->data));
00173 IPC_SET_RETVAL(answer->data, rc);
00174 }
00175 }
00176 return 0;
00177 }
00178
00183 static int request_preprocess(call_t *call)
00184 {
00185 int newphid;
00186 size_t size;
00187
00188 switch (IPC_GET_METHOD(call->data)) {
00189 case IPC_M_CONNECT_ME_TO:
00190 newphid = phone_alloc();
00191 if (newphid < 0)
00192 return ELIMIT;
00193
00194 IPC_SET_ARG3(call->data, (__native)&TASK->phones[newphid]);
00195 call->flags |= IPC_CALL_CONN_ME_TO;
00196 call->private = newphid;
00197 break;
00198 case IPC_M_AS_AREA_SEND:
00199 size = as_get_size(IPC_GET_ARG1(call->data));
00200 if (!size) {
00201 return EPERM;
00202 }
00203 IPC_SET_ARG2(call->data, size);
00204 break;
00205 default:
00206 break;
00207 }
00208 return 0;
00209 }
00210
00211
00212
00213
00214
00215
00217 static void process_answer(call_t *call)
00218 {
00219 if (IPC_GET_RETVAL(call->data) == EHANGUP && \
00220 call->flags & IPC_CALL_FORWARDED)
00221 IPC_SET_RETVAL(call->data, EFORWARD);
00222
00223 if (call->flags & IPC_CALL_CONN_ME_TO) {
00224 if (IPC_GET_RETVAL(call->data))
00225 phone_dealloc(call->private);
00226 else
00227 IPC_SET_ARG3(call->data, call->private);
00228 }
00229 }
00230
00235 static int process_request(answerbox_t *box,call_t *call)
00236 {
00237 int phoneid;
00238
00239 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) {
00240 phoneid = phone_alloc();
00241 if (phoneid < 0) {
00242 IPC_SET_RETVAL(call->data, ELIMIT);
00243 ipc_answer(box,call);
00244 return -1;
00245 }
00246 IPC_SET_ARG3(call->data, phoneid);
00247 }
00248 return 0;
00249 }
00250
00256 __native sys_ipc_call_sync_fast(__native phoneid, __native method,
00257 __native arg1, ipc_data_t *data)
00258 {
00259 call_t call;
00260 phone_t *phone;
00261 int res;
00262
00263 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
00264
00265 ipc_call_static_init(&call);
00266 IPC_SET_METHOD(call.data, method);
00267 IPC_SET_ARG1(call.data, arg1);
00268
00269 if (!(res=request_preprocess(&call))) {
00270 ipc_call_sync(phone, &call);
00271 process_answer(&call);
00272 } else
00273 IPC_SET_RETVAL(call.data, res);
00274 STRUCT_TO_USPACE(&data->args, &call.data.args);
00275
00276 return 0;
00277 }
00278
00280 __native sys_ipc_call_sync(__native phoneid, ipc_data_t *question,
00281 ipc_data_t *reply)
00282 {
00283 call_t call;
00284 phone_t *phone;
00285 int res;
00286 int rc;
00287
00288 ipc_call_static_init(&call);
00289 rc = copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
00290 if (rc != 0)
00291 return (__native) rc;
00292
00293 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
00294
00295 if (!(res=request_preprocess(&call))) {
00296 ipc_call_sync(phone, &call);
00297 process_answer(&call);
00298 } else
00299 IPC_SET_RETVAL(call.data, res);
00300
00301 rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
00302 if (rc != 0)
00303 return rc;
00304
00305 return 0;
00306 }
00307
00312 static int check_call_limit(void)
00313 {
00314 if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
00315 atomic_dec(&TASK->active_calls);
00316 return -1;
00317 }
00318 return 0;
00319 }
00320
00326 __native sys_ipc_call_async_fast(__native phoneid, __native method,
00327 __native arg1, __native arg2)
00328 {
00329 call_t *call;
00330 phone_t *phone;
00331 int res;
00332
00333 if (check_call_limit())
00334 return IPC_CALLRET_TEMPORARY;
00335
00336 GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
00337
00338 call = ipc_call_alloc(0);
00339 IPC_SET_METHOD(call->data, method);
00340 IPC_SET_ARG1(call->data, arg1);
00341 IPC_SET_ARG2(call->data, arg2);
00342 IPC_SET_ARG3(call->data, 0);
00343
00344 if (!(res=request_preprocess(call)))
00345 ipc_call(phone, call);
00346 else
00347 ipc_backsend_err(phone, call, res);
00348
00349 return (__native) call;
00350 }
00351
00356 __native sys_ipc_call_async(__native phoneid, ipc_data_t *data)
00357 {
00358 call_t *call;
00359 phone_t *phone;
00360 int res;
00361 int rc;
00362
00363 if (check_call_limit())
00364 return IPC_CALLRET_TEMPORARY;
00365
00366 GET_CHECK_PHONE(phone, phoneid, return IPC_CALLRET_FATAL);
00367
00368 call = ipc_call_alloc(0);
00369 rc = copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
00370 if (rc != 0) {
00371 ipc_call_free(call);
00372 return (__native) rc;
00373 }
00374 if (!(res=request_preprocess(call)))
00375 ipc_call(phone, call);
00376 else
00377 ipc_backsend_err(phone, call, res);
00378
00379 return (__native) call;
00380 }
00381
00389 __native sys_ipc_forward_fast(__native callid, __native phoneid,
00390 __native method, __native arg1)
00391 {
00392 call_t *call;
00393 phone_t *phone;
00394
00395 call = get_call(callid);
00396 if (!call)
00397 return ENOENT;
00398
00399 call->flags |= IPC_CALL_FORWARDED;
00400
00401 GET_CHECK_PHONE(phone, phoneid, {
00402 IPC_SET_RETVAL(call->data, EFORWARD);
00403 ipc_answer(&TASK->answerbox, call);
00404 return ENOENT;
00405 });
00406
00407 if (!is_forwardable(IPC_GET_METHOD(call->data))) {
00408 IPC_SET_RETVAL(call->data, EFORWARD);
00409 ipc_answer(&TASK->answerbox, call);
00410 return EPERM;
00411 }
00412
00413
00414
00415
00416 if (is_system_method(IPC_GET_METHOD(call->data))) {
00417 if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME)
00418 phone_dealloc(IPC_GET_ARG3(call->data));
00419
00420 IPC_SET_ARG1(call->data, method);
00421 IPC_SET_ARG2(call->data, arg1);
00422 } else {
00423 IPC_SET_METHOD(call->data, method);
00424 IPC_SET_ARG1(call->data, arg1);
00425 }
00426
00427 return ipc_forward(call, phone, &TASK->answerbox);
00428 }
00429
00431 __native sys_ipc_answer_fast(__native callid, __native retval,
00432 __native arg1, __native arg2)
00433 {
00434 call_t *call;
00435 ipc_data_t saved_data;
00436 int saveddata = 0;
00437 int rc;
00438
00439
00440 if (callid & IPC_CALLID_NOTIFICATION)
00441 return 0;
00442
00443 call = get_call(callid);
00444 if (!call)
00445 return ENOENT;
00446
00447 if (answer_need_old(call)) {
00448 memcpy(&saved_data, &call->data, sizeof(call->data));
00449 saveddata = 1;
00450 }
00451
00452 IPC_SET_RETVAL(call->data, retval);
00453 IPC_SET_ARG1(call->data, arg1);
00454 IPC_SET_ARG2(call->data, arg2);
00455 rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
00456
00457 ipc_answer(&TASK->answerbox, call);
00458 return rc;
00459 }
00460
00462 __native sys_ipc_answer(__native callid, ipc_data_t *data)
00463 {
00464 call_t *call;
00465 ipc_data_t saved_data;
00466 int saveddata = 0;
00467 int rc;
00468
00469
00470 if (callid & IPC_CALLID_NOTIFICATION)
00471 return 0;
00472
00473 call = get_call(callid);
00474 if (!call)
00475 return ENOENT;
00476
00477 if (answer_need_old(call)) {
00478 memcpy(&saved_data, &call->data, sizeof(call->data));
00479 saveddata = 1;
00480 }
00481 rc = copy_from_uspace(&call->data.args, &data->args,
00482 sizeof(call->data.args));
00483 if (rc != 0)
00484 return rc;
00485
00486 rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
00487
00488 ipc_answer(&TASK->answerbox, call);
00489
00490 return rc;
00491 }
00492
00496 __native sys_ipc_hangup(int phoneid)
00497 {
00498 phone_t *phone;
00499
00500 GET_CHECK_PHONE(phone, phoneid, return ENOENT);
00501
00502 if (ipc_phone_hangup(phone))
00503 return -1;
00504
00505 return 0;
00506 }
00507
00516 __native sys_ipc_wait_for_call(ipc_data_t *calldata, __u32 usec, int flags)
00517 {
00518 call_t *call;
00519
00520 restart:
00521 call = ipc_wait_for_call(&TASK->answerbox, usec, flags | SYNCH_FLAGS_INTERRUPTIBLE);
00522 if (!call)
00523 return 0;
00524
00525 if (call->flags & IPC_CALL_NOTIF) {
00526 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
00527
00528
00529 call->data.phone = (void *)call->private;
00530
00531 STRUCT_TO_USPACE(calldata, &call->data);
00532
00533 ipc_call_free(call);
00534
00535 return ((__native)call) | IPC_CALLID_NOTIFICATION;
00536 }
00537
00538 if (call->flags & IPC_CALL_ANSWERED) {
00539 process_answer(call);
00540
00541 ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
00542
00543 atomic_dec(&TASK->active_calls);
00544
00545 if (call->flags & IPC_CALL_DISCARD_ANSWER) {
00546 ipc_call_free(call);
00547 goto restart;
00548 }
00549
00550 STRUCT_TO_USPACE(&calldata->args, &call->data.args);
00551 ipc_call_free(call);
00552
00553 return ((__native)call) | IPC_CALLID_ANSWERED;
00554 }
00555
00556 if (process_request(&TASK->answerbox, call))
00557 goto restart;
00558
00559
00560
00561 if (STRUCT_TO_USPACE(calldata, &call->data)) {
00562 return 0;
00563 }
00564 return (__native)call;
00565 }
00566
00568 __native sys_ipc_register_irq(int irq, irq_code_t *ucode)
00569 {
00570 if (!(cap_get(TASK) & CAP_IRQ_REG))
00571 return EPERM;
00572
00573 if (irq >= IRQ_COUNT || irq <= -IPC_IRQ_RESERVED_VIRTUAL)
00574 return (__native) ELIMIT;
00575
00576 irq_ipc_bind_arch(irq);
00577
00578 return ipc_irq_register(&TASK->answerbox, irq, ucode);
00579 }
00580
00581
00582 __native sys_ipc_unregister_irq(int irq)
00583 {
00584 if (!(cap_get(TASK) & CAP_IRQ_REG))
00585 return EPERM;
00586
00587 if (irq >= IRQ_COUNT || irq <= -IPC_IRQ_RESERVED_VIRTUAL)
00588 return (__native) ELIMIT;
00589
00590 ipc_irq_unregister(&TASK->answerbox, irq);
00591
00592 return 0;
00593 }
00594