sysipc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2006 Ondrej Palkovsky
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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; /* This message is meant only for the receiver */
00076         return 1;
00077 }
00078 
00079 /****************************************************/
00080 /* Functions that preprocess answer before sending 
00081  * it to the recepient
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                 /* In case of forward, hangup the forwared phone,
00110                  * not the originator
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                         /* The connection was not accepted */
00129                         phone_dealloc(phoneid);
00130                 } else {
00131                         /* The connection was accepted */
00132                         phone_connect(phoneid,&answer->sender->answerbox);
00133                         /* Set 'phone identification' as arg3 of response */
00134                         IPC_SET_ARG3(answer->data, (__native)&TASK->phones[phoneid]);
00135                 }
00136         } else if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_ME_TO) {
00137                 /* If the users accepted call, connect */
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)) { /* Accepted, handle as_area receipt */
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                 /* Set arg3 for server */
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 /* Functions called to process received call/answer 
00213  * before passing to uspace
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) { /* Failed to allocate phone */
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         /* Userspace is not allowed to change method of system methods
00414          * on forward, allow changing ARG1 and ARG2 by means of method and arg1
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         /* Do not answer notification callids */
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         /* Do not answer notification callids */
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                 /* Set in_phone_hash to the interrupt counter */
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         /* Include phone address('id') of the caller in the request,
00560          * copy whole call->data, not only call->data.args */
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 /* Disconnect irq handler from task */
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 

Generated on Sun Jun 18 17:28:03 2006 for HelenOS Kernel (ppc64) by  doxygen 1.4.6