Subversion Repositories HelenOS-historic

Rev

Rev 1029 | 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.  */
  44.  
  45. /** Return true if the method is a system method */
  46. static inline int is_system_method(__native method)
  47. {
  48.     if (method <= IPC_M_LAST_SYSTEM)
  49.         return 1;
  50.     return 0;
  51. }
  52.  
  53. /** Return true if the message with this method is forwardable
  54.  *
  55.  * - some system messages may be forwarded, for some of them
  56.  *   it is useless
  57.  */
  58. static inline int is_forwardable(__native method)
  59. {
  60.     return 1;
  61. }
  62.  
  63. /** Find call_t * in call table according to callid
  64.  *
  65.  * @return NULL on not found, otherwise pointer to call structure
  66.  */
  67. static inline call_t * get_call(__native callid)
  68. {
  69.     /* TODO: Traverse list of dispatched calls and find one */
  70.     /* TODO: locking of call, ripping it from dispatched calls etc.  */
  71.     return (call_t *) callid;
  72. }
  73.  
  74. /** Return pointer to phone identified by phoneid or NULL if non-existent */
  75. static phone_t * get_phone(__native phoneid)
  76. {
  77.     phone_t *phone;
  78.  
  79.     if (phoneid >= IPC_MAX_PHONES)
  80.         return NULL;
  81.  
  82.     phone = &TASK->phones[phoneid];
  83.     if (!phone->callee)
  84.         return NULL;
  85.     return phone;
  86. }
  87.  
  88. /** Allocate new phone slot in current TASK structure */
  89. static int phone_alloc(void)
  90. {
  91.     int i;
  92.  
  93.     spinlock_lock(&TASK->lock);
  94.    
  95.     for (i=0; i < IPC_MAX_PHONES; i++) {
  96.         if (!TASK->phones[i].busy) {
  97.             TASK->phones[i].busy = 1;
  98.             break;
  99.         }
  100.     }
  101.     spinlock_unlock(&TASK->lock);
  102.  
  103.     if (i >= IPC_MAX_PHONES)
  104.         return -1;
  105.     return i;
  106. }
  107.  
  108. /** Disconnect phone */
  109. static void phone_dealloc(int phoneid)
  110. {
  111.     spinlock_lock(&TASK->lock);
  112.  
  113.     ASSERT(TASK->phones[phoneid].busy);
  114.  
  115.     if (TASK->phones[phoneid].callee)
  116.         ipc_phone_destroy(&TASK->phones[phoneid]);
  117.  
  118.     TASK->phones[phoneid].busy = 0;
  119.     spinlock_unlock(&TASK->lock);
  120. }
  121.  
  122. static void phone_connect(int phoneid, answerbox_t *box)
  123. {
  124.     phone_t *phone = &TASK->phones[phoneid];
  125.    
  126.     ipc_phone_connect(phone, box);
  127. }
  128.  
  129. /****************************************************/
  130. /* Functions that preprocess answer before sending
  131.  * it to the recepient
  132.  */
  133.  
  134. /** Return true if the caller (ipc_answer) should save
  135.  * the old call contents and call answer_preprocess
  136.  */
  137. static inline int answer_will_preprocess(call_t *call)
  138. {
  139.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME)
  140.         return 1;
  141.     return 0;
  142. }
  143.  
  144. /** Interpret process answer as control information */
  145. static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
  146. {
  147.     int phoneid;
  148.  
  149.     if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECTTOME) {
  150.         phoneid = IPC_GET_ARG3(*olddata);
  151.         if (IPC_GET_RETVAL(answer->data)) {
  152.             /* The connection was not accepted */
  153.             phone_dealloc(phoneid);
  154.         } else {
  155.             /* The connection was accepted */
  156.             phone_connect(phoneid,&answer->sender->answerbox);
  157.         }
  158.     }
  159. }
  160.  
  161. /****************************************************/
  162. /* Functions called to process received call/answer
  163.  * before passing to uspace
  164.  */
  165.  
  166. /** Do basic kernel processing of received call answer */
  167. static int process_answer(answerbox_t *box,call_t *call)
  168. {
  169.     return 0;
  170. }
  171.  
  172. /** Do basic kernel processing of received call request
  173.  *
  174.  * @return 0 - the call should be passed to userspace, 1 - ignore call
  175.  */
  176. static int process_request(answerbox_t *box,call_t *call)
  177. {
  178.     int phoneid;
  179.  
  180.     if (IPC_GET_METHOD(call->data) == IPC_M_CONNECTTOME) {
  181.         phoneid = phone_alloc();
  182.         if (phoneid < 0) { /* Failed to allocate phone */
  183.             IPC_SET_RETVAL(call->data, ELIMIT);
  184.             ipc_answer(box,call);
  185.             return -1;
  186.         }
  187.         IPC_SET_ARG3(call->data, phoneid);
  188.     }
  189.     return 0;
  190. }
  191.  
  192. /** Send a call over IPC, wait for reply, return to user
  193.  *
  194.  * @return Call identification, returns -1 on fatal error,
  195.            -2 on 'Too many async request, handle answers first
  196.  */
  197. __native sys_ipc_call_sync_fast(__native phoneid, __native method,
  198.                 __native arg1, __native *data)
  199. {
  200.     call_t call;
  201.     phone_t *phone;
  202.  
  203.     phone = get_phone(phoneid);
  204.     if (!phone)
  205.         return ENOENT;
  206.  
  207.     if (is_system_method(method))
  208.         return EPERM;
  209.  
  210.     ipc_call_init(&call);
  211.     IPC_SET_METHOD(call.data, method);
  212.     IPC_SET_ARG1(call.data, arg1);
  213.    
  214.     ipc_call_sync(phone, &call);
  215.  
  216.     copy_to_uspace(data, &call.data, sizeof(call.data));
  217.  
  218.     return 0;
  219. }
  220.  
  221. /** Synchronous IPC call allowing to send whole message */
  222. __native sys_ipc_call_sync(__native phoneid, __native *question,
  223.                __native *reply)
  224. {
  225.     call_t call;
  226.     phone_t *phone;
  227.  
  228.     phone = get_phone(phoneid);
  229.     if (!phone)
  230.         return ENOENT;
  231.  
  232.     ipc_call_init(&call);
  233.     copy_from_uspace(&call.data, question, sizeof(call.data));
  234.  
  235.     if (is_system_method(IPC_GET_METHOD(call.data)))
  236.         return EPERM;
  237.    
  238.     ipc_call_sync(phone, &call);
  239.  
  240.     copy_to_uspace(reply, &call.data, sizeof(call.data));
  241.  
  242.     return 0;
  243. }
  244.  
  245. /** Check that the task did not exceed allowed limit
  246.  *
  247.  * @return 0 - Limit OK,   -1 - limit exceeded
  248.  */
  249. static int check_call_limit(void)
  250. {
  251.     if (atomic_preinc(&TASK->active_calls) > IPC_MAX_ASYNC_CALLS) {
  252.         atomic_dec(&TASK->active_calls);
  253.         return -1;
  254.     }
  255.     return 0;
  256. }
  257.  
  258. /** Send an asynchronous call over ipc
  259.  *
  260.  * @return Call identification, returns -1 on fatal error,
  261.            -2 on 'Too many async request, handle answers first
  262.  */
  263. __native sys_ipc_call_async_fast(__native phoneid, __native method,
  264.                  __native arg1, __native arg2)
  265. {
  266.     call_t *call;
  267.     phone_t *phone;
  268.  
  269.     phone = get_phone(phoneid);
  270.     if (!phone)
  271.         return IPC_CALLRET_FATAL;
  272.  
  273.     if (is_system_method(method))
  274.         return IPC_CALLRET_FATAL;
  275.  
  276.     if (check_call_limit())
  277.         return IPC_CALLRET_TEMPORARY;
  278.  
  279.     call = ipc_call_alloc();
  280.     IPC_SET_METHOD(call->data, method);
  281.     IPC_SET_ARG1(call->data, arg1);
  282.     IPC_SET_ARG2(call->data, arg2);
  283.  
  284.     ipc_call(phone, call);
  285.  
  286.     return (__native) call;
  287. }
  288.  
  289. /** Synchronous IPC call allowing to send whole message
  290.  *
  291.  * @return The same as sys_ipc_call_async
  292.  */
  293. __native sys_ipc_call_async(__native phoneid, __native *data)
  294. {
  295.     call_t *call;
  296.     phone_t *phone;
  297.  
  298.     phone = get_phone(phoneid);
  299.     if (!phone)
  300.         return IPC_CALLRET_FATAL;
  301.  
  302.     if (check_call_limit())
  303.         return IPC_CALLRET_TEMPORARY;
  304.  
  305.     call = ipc_call_alloc();
  306.     copy_from_uspace(&call->data, data, sizeof(call->data));
  307.  
  308.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  309.         ipc_call_free(call);
  310.         return EPERM;
  311.     }
  312.    
  313.     ipc_call(phone, call);
  314.  
  315.     return (__native) call;
  316. }
  317.  
  318. /** Forward received call to another destination
  319.  *
  320.  * The arg1 and arg2 are changed in the forwarded message
  321.  */
  322. __native sys_ipc_forward_fast(__native callid, __native phoneid,
  323.                   __native method, __native arg1)
  324. {
  325.     call_t *call;
  326.     phone_t *phone;
  327.  
  328.     call = get_call(callid);
  329.     if (!call)
  330.         return ENOENT;
  331.  
  332.     phone = get_phone(phoneid);
  333.     if (!phone) {
  334.         IPC_SET_RETVAL(call->data, EFORWARD);
  335.         ipc_answer(&TASK->answerbox, call);
  336.         return ENOENT;
  337.     }
  338.  
  339.     if (!is_forwardable(IPC_GET_METHOD(call->data))) {
  340.         IPC_SET_RETVAL(call->data, EFORWARD);
  341.         ipc_answer(&TASK->answerbox, call);
  342.         return EPERM;
  343.     }
  344.  
  345.     /* Userspace is not allowed to change method of system methods
  346.      * on forward, allow changing ARG1 and ARG2 by means of method and arg1
  347.      */
  348.     if (is_system_method(IPC_GET_METHOD(call->data))) {
  349.         IPC_SET_ARG1(call->data, method);
  350.         IPC_SET_ARG2(call->data, arg1);
  351.     } else {
  352.         IPC_SET_METHOD(call->data, method);
  353.         IPC_SET_ARG1(call->data, arg1);
  354.     }
  355.  
  356.     ipc_forward(call, phone->callee, &TASK->answerbox);
  357.  
  358.     return 0;
  359. }
  360.  
  361. /** Send IPC answer */
  362. __native sys_ipc_answer_fast(__native callid, __native retval,
  363.                  __native arg1, __native arg2)
  364. {
  365.     call_t *call;
  366.     ipc_data_t saved_data;
  367.     int preprocess = 0;
  368.  
  369.     call = get_call(callid);
  370.     if (!call)
  371.         return ENOENT;
  372.  
  373.     if (answer_will_preprocess(call)) {
  374.         memcpy(&saved_data, &call->data, sizeof(call->data));
  375.         preprocess = 1;
  376.     }
  377.  
  378.     IPC_SET_RETVAL(call->data, retval);
  379.     IPC_SET_ARG1(call->data, arg1);
  380.     IPC_SET_ARG2(call->data, arg2);
  381.  
  382.     if (preprocess)
  383.         answer_preprocess(call, &saved_data);
  384.  
  385.     ipc_answer(&TASK->answerbox, call);
  386.     return 0;
  387. }
  388.  
  389. /** Send IPC answer */
  390. inline __native sys_ipc_answer(__native callid, __native *data)
  391. {
  392.     call_t *call;
  393.     ipc_data_t saved_data;
  394.     int preprocess = 0;
  395.  
  396.     call = get_call(callid);
  397.     if (!call)
  398.         return ENOENT;
  399.  
  400.     if (answer_will_preprocess(call)) {
  401.         memcpy(&saved_data, &call->data, sizeof(call->data));
  402.         preprocess = 1;
  403.     }
  404.     copy_from_uspace(&call->data, data, sizeof(call->data));
  405.  
  406.     if (preprocess)
  407.         answer_preprocess(call, &saved_data);
  408.    
  409.     ipc_answer(&TASK->answerbox, call);
  410.  
  411.     return 0;
  412. }
  413.  
  414. /** Ask the other side of connection to do 'callback' connection
  415.  *
  416.  * @return 0 if no error, error otherwise
  417.  */
  418. __native sys_ipc_connect_to_me(__native phoneid, __native arg1,
  419.                    __native arg2, task_id_t *taskid)
  420. {
  421.     call_t call;
  422.     phone_t *phone;
  423.  
  424.     phone = get_phone(phoneid);
  425.     if (!phone)
  426.         return ENOENT;
  427.  
  428.     ipc_call_init(&call);
  429.     IPC_SET_METHOD(call.data, IPC_M_CONNECTTOME);
  430.     IPC_SET_ARG1(call.data, arg1);
  431.     IPC_SET_ARG2(call.data, arg2);
  432.    
  433.     ipc_call_sync(phone, &call);
  434.  
  435.     if (!IPC_GET_RETVAL(call.data) && taskid)
  436.         copy_to_uspace(taskid,
  437.                    &phone->callee->task->taskid,
  438.                    sizeof(TASK->taskid));
  439.     return IPC_GET_RETVAL(call.data);
  440. }
  441.  
  442. /** Ask target process to connect me somewhere
  443.  *
  444.  * @return phoneid - on success, error otherwise
  445.  */
  446. __native sys_ipc_connect_me_to(__native phoneid, __native arg1,
  447.                    __native arg2)
  448. {
  449.     call_t call;
  450.     phone_t *phone;
  451.  
  452.     phone = get_phone(phoneid);
  453.     if (!phone)
  454.         return ENOENT;
  455.  
  456.     ipc_call_init(&call);
  457.     IPC_SET_METHOD(call.data, IPC_M_CONNECTMETO);
  458.     IPC_SET_ARG1(call.data, arg1);
  459.     IPC_SET_ARG2(call.data, arg2);
  460.  
  461.     ipc_call_sync(phone, &call);
  462.     if (!IPC_GET_RETVAL(call.data)) {
  463.         /* Everybody accepted, we should be connected by now */
  464.     }
  465.  
  466.     return 0;
  467. }
  468.  
  469. /** Wait for incoming ipc call or answer
  470.  *
  471.  * Generic function - can serve either as inkernel or userspace call
  472.  * - inside kernel does probably unnecessary copying of data (TODO)
  473.  *
  474.  * @param result
  475.  * @param taskid
  476.  * @param flags
  477.  * @return Callid, if callid & 1, then the call is answer
  478.  */
  479. inline __native sys_ipc_wait_for_call(ipc_data_t *calldata,
  480.                       task_id_t *taskid,
  481.                       __native flags)
  482.                          
  483. {
  484.     call_t *call;
  485.  
  486. restart:   
  487.     call = ipc_wait_for_call(&TASK->answerbox, flags);
  488.     printf("Received call %P from sender: %P\n", call, call->sender);
  489.  
  490.     if (call->flags & IPC_CALL_ANSWERED) {
  491.         if (process_answer(&TASK->answerbox, call))
  492.             goto restart;
  493.  
  494.         copy_to_uspace(calldata, &call->data, sizeof(call->data));
  495.         atomic_dec(&TASK->active_calls);
  496.         ASSERT(! (call->flags & IPC_CALL_STATIC_ALLOC));
  497.         ipc_call_free(call);
  498.  
  499.         return ((__native)call) | IPC_CALLID_ANSWERED;
  500.     }
  501.     if (process_request(&TASK->answerbox, call))
  502.         goto restart;
  503.     copy_to_uspace(calldata, &call->data, sizeof(call->data));
  504.     copy_to_uspace(taskid, (void *)&TASK->taskid, sizeof(TASK->taskid));
  505.     return (__native)call;
  506. }
  507.  
  508.