Subversion Repositories HelenOS-historic

Rev

Rev 1027 | Rev 1060 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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.  
  32. #include <errno.h>
  33. #include <mm/page.h>
  34. #include <memstr.h>
  35. #include <debug.h>
  36. #include <ipc/ipc.h>
  37. #include <ipc/sysipc.h>
  38. #include <print.h>
  39.  
  40. /* TODO: multi-threaded connect-to-me can cause race condition
  41.  * on phone, add counter + thread_kill??
  42.  *
  43.  * - don't allow userspace app to send system messages
  44.  */
  45.  
  46. /** Return pointer to phone identified by phoneid or NULL if non-existent */
  47. static phone_t * get_phone(__native phoneid)
  48. {
  49.     phone_t *phone;
  50.  
  51.     if (phoneid >= IPC_MAX_PHONES)
  52.         return NULL;
  53.  
  54.     phone = &TASK->phones[phoneid];
  55.     if (!phone->callee)
  56.         return NULL;
  57.     return phone;
  58. }
  59.  
  60. /** Allocate new phone slot in current TASK structure */
  61. static int phone_alloc(answerbox_t *callee)
  62. {
  63.     int i;
  64.  
  65.     spinlock_lock(&TASK->lock);
  66.    
  67.     for (i=0; i < IPC_MAX_PHONES; i++) {
  68.         if (!TASK->phones[i].callee) {
  69.             TASK->phones[i].callee = callee;
  70.             break;
  71.         }
  72.     }
  73.     spinlock_unlock(&TASK->lock);
  74.  
  75.     if (i >= IPC_MAX_PHONES)
  76.         return -1;
  77.     return i;
  78. }
  79.  
  80. /** Disconnect phone */
  81. static void phone_dealloc(int phoneid)
  82. {
  83.     spinlock_lock(&TASK->lock);
  84.  
  85.     ASSERT(TASK->phones[phoneid].callee);
  86.  
  87.     TASK->phones[phoneid].callee = NULL;
  88.     spinlock_unlock(&TASK->lock);
  89. }
  90.  
  91. /****************************************************/
  92. /* Functions that preprocess answer before sending
  93.  * it to the recepient
  94.  */
  95.  
  96. /** Return true if the caller (ipc_answer) should save
  97.  * the old call contents and call answer_preprocess
  98.  */
  99. static inline int answer_will_preprocess(call_t *call)
  100. {
  101.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME)
  102.         return 1;
  103.     return 0;
  104. }
  105.  
  106. /** Interpret process answer as control information */
  107. static inline void answer_preprocess(call_t *answer, call_t *call)
  108. {
  109.     int phoneid;
  110.  
  111.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
  112.         if (IPC_GET_RETVAL(answer->data)) {
  113.             /* The connection was not accepted */
  114.             /* TODO...race for multi-threaded process */
  115.             phoneid = IPC_GET_ARG3(call->data);
  116.             phone_dealloc(phoneid);
  117.         }
  118.     }
  119. }
  120.  
  121. /****************************************************/
  122. /* Functions called to process received call/answer
  123.  * before passing to uspace
  124.  */
  125.  
  126. /** Do basic kernel processing of received call answer */
  127. static int process_answer(answerbox_t *box,call_t *call)
  128. {
  129.     return 0;
  130. }
  131.  
  132. /** Do basic kernel processing of received call request
  133.  *
  134.  * @return 0 - the call should be passed to userspace, 1 - ignore call
  135.  */
  136. static int process_request(answerbox_t *box,call_t *call)
  137. {
  138.     int phoneid;
  139.  
  140.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
  141.         phoneid = phone_alloc(&call->sender->answerbox);
  142.         if (phoneid < 0) { /* Failed to allocate phone */
  143.             IPC_SET_RETVAL(call->data, ELIMIT);
  144.             ipc_answer(box,call);
  145.             return -1;
  146.         }
  147.         IPC_SET_ARG3(call->data, phoneid);
  148.     }
  149.     return 0;
  150. }
  151.  
  152. /** Send a call over IPC, wait for reply, return to user
  153.  *
  154.  * @return Call identification, returns -1 on fatal error,
  155.            -2 on 'Too many async request, handle answers first
  156.  */
  157. __native sys_ipc_call_sync_fast(__native phoneid, __native method,
  158.                 __native arg1, __native *data)
  159. {
  160.     call_t call;
  161.     phone_t *phone;
  162.  
  163.     phone = get_phone(phoneid);
  164.     if (!phone)
  165.         return IPC_CALLRET_FATAL;
  166.  
  167.     ipc_call_init(&call);
  168.     IPC_SET_METHOD(call.data, method);
  169.     IPC_SET_ARG1(call.data, arg1);
  170.    
  171.     ipc_call_sync(phone, &call);
  172.  
  173.     copy_to_uspace(data, &call.data, sizeof(call.data));
  174.  
  175.     return 0;
  176. }
  177.  
  178. /** Synchronous IPC call allowing to send whole message */
  179. __native sys_ipc_call_sync(__native phoneid, __native *question,
  180.                __native *reply)
  181. {
  182.     call_t call;
  183.     phone_t *phone;
  184.  
  185.     phone = get_phone(phoneid);
  186.     if (!phone)
  187.         return IPC_CALLRET_FATAL;
  188.  
  189.     ipc_call_init(&call);
  190.     copy_from_uspace(&call.data, question, sizeof(call.data));
  191.    
  192.     ipc_call_sync(phone, &call);
  193.  
  194.     copy_to_uspace(reply, &call.data, sizeof(call.data));
  195.  
  196.     return 0;
  197. }
  198.  
  199. /** Check that the task did not exceed allowed limit
  200.  *
  201.  * @return 0 - Limit OK,   -1 - limit exceeded
  202.  */
  203. static int check_call_limit(void)
  204. {
  205.     if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
  206.         atomic_dec(&TASK->active_calls);
  207.         return -1;
  208.     }
  209.     return 0;
  210. }
  211.  
  212. /** Send an asynchronous call over ipc
  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_async_fast(__native phoneid, __native method,
  218.                  __native arg1, __native arg2)
  219. {
  220.     call_t *call;
  221.     phone_t *phone;
  222.  
  223.     phone = get_phone(phoneid);
  224.     if (!phone)
  225.         return IPC_CALLRET_FATAL;
  226.  
  227.     if (check_call_limit())
  228.         return IPC_CALLRET_TEMPORARY;
  229.  
  230.     call = ipc_call_alloc();
  231.     IPC_SET_METHOD(call->data, method);
  232.     IPC_SET_ARG1(call->data, arg1);
  233.     IPC_SET_ARG2(call->data, arg2);
  234.  
  235.     ipc_call(phone, call);
  236.  
  237.     return (__native) call;
  238. }
  239.  
  240. /** Synchronous IPC call allowing to send whole message
  241.  *
  242.  * @return The same as sys_ipc_call_async
  243.  */
  244. __native sys_ipc_call_async(__native phoneid, __native *data)
  245. {
  246.     call_t *call;
  247.     phone_t *phone;
  248.  
  249.     phone = get_phone(phoneid);
  250.     if (!phone)
  251.         return IPC_CALLRET_FATAL;
  252.  
  253.     if (check_call_limit())
  254.         return IPC_CALLRET_TEMPORARY;
  255.  
  256.     call = ipc_call_alloc();
  257.     copy_from_uspace(&call->data, data, sizeof(call->data));
  258.    
  259.     ipc_call(phone, call);
  260.  
  261.     return (__native) call;
  262. }
  263.  
  264. /** Send IPC answer */
  265. __native sys_ipc_answer_fast(__native callid, __native retval,
  266.                  __native arg1, __native arg2)
  267. {
  268.     call_t *call;
  269.     call_t saved_call;
  270.     int preprocess = 0;
  271.  
  272.     /* Check that the user is not sending us answer callid */
  273.     ASSERT(! (callid & 1));
  274.     /* TODO: Check that the callid is in the dispatch table */
  275.     call = (call_t *) callid;
  276.  
  277.     if (answer_will_preprocess(call)) {
  278.         memcpy(&saved_call.data, &call->data, sizeof(call->data));
  279.         preprocess = 1;
  280.     }
  281.  
  282.     IPC_SET_RETVAL(call->data, retval);
  283.     IPC_SET_ARG1(call->data, arg1);
  284.     IPC_SET_ARG2(call->data, arg2);
  285.     if (preprocess)
  286.         answer_preprocess(call, &saved_call);
  287.  
  288.     ipc_answer(&TASK->answerbox, call);
  289.     return 0;
  290. }
  291.  
  292. /** Send IPC answer */
  293. inline __native sys_ipc_answer(__native callid, __native *data)
  294. {
  295.     call_t *call;
  296.     call_t saved_call;
  297.     int preprocess = 0;
  298.  
  299.     /* Check that the user is not sending us answer callid */
  300.     ASSERT(! (callid & 1));
  301.     /* TODO: Check that the callid is in the dispatch table */
  302.     call = (call_t *) callid;
  303.  
  304.     if (answer_will_preprocess(call)) {
  305.         memcpy(&saved_call.data, &call->data, sizeof(call->data));
  306.         preprocess = 1;
  307.     }
  308.     copy_from_uspace(&call->data, data, sizeof(call->data));
  309.  
  310.     if (preprocess)
  311.         answer_preprocess(call, &saved_call);
  312.    
  313.     ipc_answer(&TASK->answerbox, call);
  314.  
  315.     return 0;
  316. }
  317.  
  318. /** Ask the other side of connection to do 'callback' connection
  319.  *
  320.  * @return 0 if no error, error otherwise
  321.  */
  322. __native sys_ipc_connect_to_me(__native phoneid, __native arg1,
  323.                    __native arg2, task_id_t *taskid)
  324. {
  325.     call_t call;
  326.     phone_t *phone;
  327.  
  328.     phone = get_phone(phoneid);
  329.     if (!phone)
  330.         return IPC_CALLRET_FATAL;
  331.  
  332.     ipc_call_init(&call);
  333.     IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME);
  334.     IPC_SET_ARG1(call.data, arg1);
  335.     IPC_SET_ARG2(call.data, arg2);
  336.    
  337.     ipc_call_sync(phone, &call);
  338.  
  339.     if (!IPC_GET_RETVAL(call.data) && taskid)
  340.         copy_to_uspace(taskid,
  341.                    &phone->callee->task->taskid,
  342.                    sizeof(TASK->taskid));
  343.     return IPC_GET_RETVAL(call.data);
  344. }
  345.  
  346. /** Wait for incoming ipc call or answer
  347.  *
  348.  * Generic function - can serve either as inkernel or userspace call
  349.  * - inside kernel does probably unnecessary copying of data (TODO)
  350.  *
  351.  * @param result
  352.  * @param taskid
  353.  * @param flags
  354.  * @return Callid, if callid & 1, then the call is answer
  355.  */
  356. inline __native sys_ipc_wait_for_call(ipc_data_t *calldata,
  357.                       task_id_t *taskid,
  358.                       __native flags)
  359.                          
  360. {
  361.     call_t *call;
  362.  
  363. restart:   
  364.     call = ipc_wait_for_call(&TASK->answerbox, flags);
  365.  
  366.     if (call->flags & IPC_CALL_ANSWERED) {
  367.         if (process_answer(&TASK->answerbox, call))
  368.             goto restart;
  369.  
  370.         copy_to_uspace(calldata, &call->data, sizeof(call->data));
  371.         atomic_dec(&TASK->active_calls);
  372.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  373.         ipc_call_free(call);
  374.  
  375.         return ((__native)call) | IPC_CALLID_ANSWERED;
  376.     }
  377.     if (process_request(&TASK->answerbox, call))
  378.         goto restart;
  379.     copy_to_uspace(calldata, &call->data, sizeof(call->data));
  380.     copy_to_uspace(taskid, (void *)&TASK->taskid, sizeof(TASK->taskid));
  381.     return (__native)call;
  382. }
  383.  
  384.